ホーム ブログ ページ 29

Kotlinの便利機能

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

Kotlinでよく使う便利機能を紹介します

lateinit

遅延初期化に使います。

class Sample : AppCompatActivity() {
    private lateinit var name: String

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        name = intent.getStringExtra("WAGASHI") ?: "Daihuku" // エルビス演算子
        println(name)
    }
}

みたいに使います。
Javaだとメンバ変数で

private String name = ""

みたいな書き方をすると思うんですが、これだと再代入する前に呼び出されると空文字が返ってしまうのでよくないですね。
ちなみにKotlinで初期化する前にnameを呼び出すと落ちます。

オプショナル

val name = null
name.toUpperCase()

みたいなことをすると落ちます。

val name = null
name?.toUpperCase()

にすると何も起こらないため落ちないです。

他にも、

val num = 1
num as String   //落ちる
num as? String   //nullになる 

?を使うとこんな感じになります。

型を調べる

Javaだと、.getClass()で確認できました。
KotlinにはgetClass()が無いので、

val num = 1  
println(num.javaClass) //Javaの型が取得できる  
println(num.javaClass.kotlin) //Kotlinの型が取得できる  

.javaClassだとどんな型か調べることができました。
特定の型かどうかを調べたいときには、 is が使えます。

val num = 1
println(num is Int) // True

これはJavaではinstanceOf()を使っていた処理です。

テンプレート式

文字列の中に変数を入れられます。

// Java
Integer num = 1;
println("num is " + num);

// Kotlin
val num = 1
println("num is $num")

// 長い処理
println("num x2 is ${num * 2}")

AndroiStudioのConvert Java File to Kotlin File

これはKotlinの機能ではないんですが、Android StudioにはJavaをKotlinに変換する機能があります。
Code -> Convert Java File to Kotlin File
これが凄く便利です。
何か処理をしたくてサンプルコードを探したけどJavaしかないってときにJavaのコードをKotlinに変換します。
コンパイル時や実行時にエラーが出ることは経験上ないのでおすすめ。

まとめ

Kotlinを使うとNPEが起こりにくい書き方ができます。

また、同じ処理でもJavaに比べると簡潔に書けるのが魅力です。

今回は書きませんでしたが、コレクションの種類や操作関数も豊富なのでコード量が減らせたり安全な設計ができます。

Apacheを最新版に更新したらnagiosのhttp監視がBad Requestになった話

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。


こんにちは。アピリッツの本多です。 今回は先日apacheを最新版に更新した際に発生した事象についてご紹介します。 こちらはAWS上で稼働しているEC2インスタンス(OSはAmazon Linux)にて発生したものですが、他の環境でも発生した際のご参考になればと思い、本記事を投稿させて頂きます。

エラー発生の経緯

2017-09-18にAmazon Linux向けに「重要度:重要」のapache(httpd)のアップデートが配信されました。
https://alas.aws.amazon.com/ALAS-2017-896.html

そのため先日、稼働中のwebサーバー(以降server_01と表記)にてapacheの更新を実施したのですが、更新直後からnagios(server_01とは別のサーバー上で稼働)の監視項目の1つとして設定しているhttp監視からアラートが発生するようになりました。

手動で確認してもこの通り400 Bad Requestが返ってきます。
apacheのアップデート前までは200 OKでした。

/usr/lib64/nagios/plugins/check_http -H server_01 -u /
=> HTTP WARNING: HTTP/1.1 400 Bad Request - 513 bytes in 0.002 second response time |time=0.001673s;;;0.000000 size=513B;;;0

nagios設定

nagiosサーバーのhostsの設定にサーバー名を書いて、nagiosのcfgファイルでaddressの部分にそのサーバー名を指定していました。

# hosts
10.0.0.1 server_01
10.0.0.2 server_02

# cfgファイル
define host{
    use                     linux-server
    host_name               server-01
    alias                   server-01
    address                 server_01
    }
~
define service{
    use                             generic-service
    host_name                       server-01
    service_description             HTTP
    check_command                   check_http! -u /
    notifications_enabled           1
    }

原因

サーバー名「server_01」にアンダーバーを使用していた事により、エラーが発生していました。

汎用JPドメイン名登録等に関する技術細則
https://jprs.jp/doc/rule/saisoku-1-wideusejp.html

「ドメイン名」は、ラベルをピリオド(”.”)で区切って連結した文字列であ
る。

「ラベル」は、本技術細則により定められるASCIIラベルおよび日本語ラベル
により構成される。

「ASCIIラベル」は、英字(”A”から”Z”)、数字(”0″から”9″)、ハイフン(”-“)
からなる文字列である。ただし、ASCIIラベルの先頭と末尾の文字はハイフン
であってはならない。

ドメイン名については上記のように定義されているのですが、どうやら今回のapacheのアップデートにて、それを厳密にチェックする判定が導入されたようです。
そのため、ドメイン名に上記の文字列以外(今回のケースではアンダーバー)が入ると、400 Bad Requestを返すようになったと思われます。

nagios設定 修正後

おとなしく、nagiosのcfgをIPアドレスに変更する事でエラーが解消されました。

# cfgファイル
define host{
    use                     linux-server
    host_name               server-01
    alias                   server-01
    address                 10.0.0.1
    }
~
define service{
    use                             generic-service
    host_name                       server-01
    service_description             HTTP
    check_command                   check_http! -u /
    notifications_enabled           1
    }

手動で実行した際の結果もこの通りです。

/usr/lib64/nagios/plugins/check_http -H 10.0.0.1 -u /
=> HTTP OK: HTTP/1.1 200 OK - 352 bytes in 0.016 second response time |time=0.016100s;;;0.000000 size=352B;;;0

まとめ

今回はapacheのエラーという形で表面化しましたが、もしかすると他のミドルウェアとの連携でもハイフンが原因で正常に動かない等の問題があるかもしれません。
サーバー名を設定する際にアンダーバーを使うのはリスクがあるため今後は使わないようにしようと思います。

シェルスクリプトと戦う〜その2〜

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

前回はシェルスクリプトの始まりの部分とディレクトリパスの確認を行いました。
今回はif文と引数、変数を利用していきます。

if文を書いてみる

---checkVariable.sh

#!bin/bash
if [ "$1" == "" ]; then
  echo "引数が設定されていません。"
else
  echo "引数は$1です。"
fi

 
enter image description here

 
if文を使う場合はifで始まり、fiでif文全体の終わりを示します。
(条件分岐にはcase文もありますが使用頻度は……)

---listVariables.sh

#!bin/bash
if [ $# == 0 ]; then
  echo "引数が設定されていません。"
else
  for variable in $@
  do
    echo "引数の1つ:$variable"
  done
fi

 
enter image description here
 
入力した引数を順々にvariableに格納し、表示していきます。ただし、引数を設定していない場合は「設定されていない」旨のメッセージを表示します。
さて、$#$@$1といった変数は、シェル実行時点で初期値が決まっています。入る値は以下の通り。
 

$#:実行するシェルファイル名の後に続く引数の数(半角スペース区切り)
$@:実行するシェルファイル名の後に続く引数全体
$1:実行するシェルファイル名の後に続く引数の1つ目。2つ目以降は$2$3・・・となる。

これがおおよそわかっていれば、引数の順序について規則を設けた上で対応した変数を利用することができます。

少し本格的に書いてみる

set -euを利用していますが、こちらを参考に。(コメントも大事なことが。)
シェルスクリプトを書くときはset -euしておく

--- copyFile.sh

#!bin/bash
#bash記述のため、cd等実行エラー時強制終了するため保護
#想定外の事故をなるべく回避するため記載
set -eu
if [ $# != 2 ]; then
  echo "引数不足、または引数過多です。コピー前とコピー後のファイル名を入力してください。"
  exit
fi

#シェル自身のディレクトリを格納する。
dirPath=$(dirname $(readlink -f $0))

#半角スペースが含まれるディレクトリの場合は""でくくることで引数の区切りとして認識されなくなる
cd "${dirPath}"

if [ -e "$1" ] && [ ! -e "$2" ]; then
  cp -a "${dirPath}/$1" "${dirPath}/$2"
  echo "${dirPath}/$1を${dirPath}/$2にコピーしました。"
elif [ ! -e "${dirPath}/$1" ]; then
  echo "コピー元のファイルがありません。"
else
  echo "コピー先のファイルが既に存在します。上書きを防ぐため、実行を中止しました。"
fi

 
enter image description here
 
引数が2つぴったりでないと実行しない、ファイルをコピーするシェルスクリプトです。引数が2つ以外の時は、最初のif文で警告文を表示し、その後exitで実行を終了します。
処理するファイルの基準はシェルの置いてあるディレクトリとします。そのためシェルのディレクトリ位置を取得し、 cd コマンドで移動しています。
 
2つ目のif文ではコピー元のファイルが存在すること、及びコピー先に指定した名前のファイルが存在しないことを確認しています。分岐が3つになったので、else ifにあたるelifを使用しています。
 

変数記述に注意

引数を順々に表示するシェルと違い、自身で設定した変数(dirPathなど)に{ }が付いています。このスクリプトでは{ }はなくとも正常に動作するのですが、例えば、

--- wrongCopy.sh

#!bin/bash
# -u オプションは後解説用にわざと外しています
set -e
dirPath=$(dirname $(readlink -f $0))
cd "${dirPath}"

if [ -e "$1.txt" ] && [ ! -e "test_$2.txt" ]; then
  echo "===== コピー開始 ====="
  #一度$2をtargetに格納する
  target=$2
  target_today=wrong

  #ここだけ外す
  cp -a "$1.txt" "test_$target_today.txt"
  echo "===== コピー終了 ====="
  echo "test_${target}_today.txt が作成されました。"
  ls -la test_*
fi

 
拡張子前の名前を引数として、これを実行した際に以下のようになります。
 
enter image description here
 
想定と違うファイルが生成されましたね。
これはtargetという変数ではなく、target_todayという変数で展開されているためです。一方で、先に出した例では/が存在するために変数名がその手前で終了していると認識されたため、正常に動作してしまいます。
$1$2なども同様の現象に陥りそうですが、こちらは元々ユーザ側では先頭が数字の変数名をつけられないため、別に認識してくれるようです。
 
正常に動作するパターン、正常に動作しないパターンを覚えてもいいですが、それよりも変数名を{ }でくくるようにした方が予期せぬ動作を起こしにくくなると思います。
また、今回target_todayの値を設定していますが、未定義の場合はtest_.txtとして生成されます。(未定義の場合にも終了させるset -uオプションがあります。)
 

存在確認前に引数の例外処理を実行

少し判定文を付け加えます。

--- copyFile.shに追加

〜省略〜
dirPath=$(dirname $(readlink -f $0))

# /と/homeだけ弾く例
if [[ "${dirPath}" =~ ^/(home/*)?$ ]]; then
  echo "現在置かれているシェルの場所では実行できません。実行するユーザのディレクトリ以下の場所に配置してください。"
  exit
else
  echo "${dirPath}"
fi

if [[ "$1" =~ "\.\." ]] || [[ "$2" =~ "\.\." ]]; then
  echo "予期しない動作を行う可能性があるため、処理を終了します。"
  exit
fi

cd "${dirPath}"
if [ -e "$1" ] && [ ! -e "$2" ]; then
〜省略〜

 
enter image description here
cpコマンド前に終了したので生成されない
 
enter image description here
実行したくないディレクトリなのかを確認
 

簡易的ですが、このディレクトリでは絶対に処理させたくないことを示すif文を最初に記載しています。前回にも書きましたが、テスト用の環境構築を行ってから実行しましょう。2つ目は1つ上のディレクトリに行く記述を禁止しています。
また、判定文として正規表現を利用しています。[の数が変わる等、変化しているので注意してください。
 

終わりに

前回締めに書いた通り、今回はif文と変数を利用した、上書きできないコピーを行うシェルを紹介しました。正規表現もパスの確認のために少し利用しました。これくらいの内容であれば、普通にコマンド入力した方がはやいのですが、様々な処理を盛り込んだり、一部同じ値を使いまわしたりする場合は、単純なコピーでも作成すると楽になるのではないでしょうか。
記事を書いている本人もしっかりしたものを書けるわけではないですが、興味や参考になればと思います。
 


関連記事:
シェルスクリプトを書くときはset -euしておく
シェルスクリプトと戦う〜その1〜

Googleデータスタジオでハイパーリンクを設定する方法

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

Googleデータポータルだと表のハイパーリンクが設定されない・・・。そうお思いですか?実はGoogleアナリティクスのように表のURLから対象ページにリンクするだけでなくより複雑なリンク指定もできるんです。

この記事でまとめられていること

こんにちは。株式会社アピリッツでアナリストをしているssekiです。
最近GoogleデータポータルとGoogleアナリティクスを交互に見ることが多くなりました。

毅然としてGoogleアナリティクスを使い続ける理由はいくつかあるのですが、こと定点観測においてもデータポータルに比べてGoogleアナリティクスで便利だなぁと感じる点がひとつあります。

それは・・・、
「表から直接URLにリンクできる」ことです。

しかしながら、その考えは間違いでした。
実は、関数を使えばGoogleデータポータルでもハイパーリンクはできるんです!!!!

ということで、今回はGoogleデータポータルの表中でハイパーリンクを使用する方法についてご紹介いたします。

HYPERLINK関数を使ってハイパーリンクをつけよう

Googleデータポータルにハイパーリンク機能をつける関数、その名もHYPERLINK関数です。
名前そのまんまなので覚えやすいですね!

早速HYPERLINK関数の使い方を見てみましょう。以下はヘルプページからの引用です。

HYPERLINK(URL、リンクのラベル)
パラメータ:
 URL – URL として評価されるフィールドまたは式。
 リンクのラベル – フィールド、式、またはリテラル値。

あいかわらず、ヘルプページは簡潔すぎて意味がよくわからないですね・・・。
では、実際に簡単な例を通して使い方をご紹介いたします!

GoogleアナリティクスのようにURLをリンクさせる方法

Googleアナリティクスでは行動>サイトコンテンツ>すべてのページなどに表示されるディメンション「ページ」の表にハイパーリンクが設置されていますよね。
enter image description here

Googleデータポータルで同じような表を作成しても、デフォルトではハイパーリンクになりません。
そこで、HYPERLINK関数の出番です。計算フィールドで以下のディメンションを作成しましょう。

HYPERLINK(ページ,ページ)

URLの部分にページを指定、リンクのラベルの部分にもページを指定してみます。
そうすると、表ではこのように表示されますね。
enter image description here
ページ(リンク可能)と書いてある列が作成したディメンションです。
察しの良い方はお分かりでしょうが、これではリンクは機能しません。

見た目上同じなのになぜ?と思うかもしれませんが、URLに指定されている部分が”/”で始まっているので、リンク先のURLとして正しくない状態です。

そこで、URLの部分を次のように書き直してみましょう。

HYPERLINK(CONCAT("https://shop.googlemerchandisestore.com",ページ),ページ)

これで見た目は同じで、リンクが機能するようになりました。

えっ?初めてみる関数がある?
そうです。Googleアナリティクスデータを用いる場合、CONCAT関数を使って初めてHYPERLINK関数は機能するのです。

CONCAT関数で複数のものを結合させよう

CONCATはconcatenationの略で、日本語にすると「連結、結合」を意味します。
その名の通り、何かと何かをつなげる役割をする関数です。

使い方はいたって簡単。
CONCAT(A,B,C,・・・)のように、連結したい文字をパラメータに指定することで「ABC・・・」という文字列が生成されます。

先ほどHYPERLINK関数の利用例では、ドメイン部分をCONCAT関数によって前部分につけ、正しいURLに差し替えたというわけです。

クロスドメイン設定をしている場合はそのままでOK

ちなみに、先ほど誤用例として挙げた式ですが、全く使えないわけではありません。
実は、Googleアナリティクスでクロスドメイン設定をしている場合には、そのまま有効なリンクになるのです!

なぜなら、クロスドメイン設定時にはディメンション「ページ」がドメインから始まる正しいURLになるためです。

むしろ、この場合Googleアナリティクスの方でハイパーリンクが機能しない分、Googleデータポータルの方が優秀ですね。恐れ入りました。

※クロスドメイン設定とは、Googleアナリティクスでクロスドメイントラッキングを設定し、レポートビュー上でクロスドメインのフィルタをかけたものを指しています。

リンク表示名を入れ替えることも可能

HYPERLINK関数を用いれば、リンク表示名(ラベル)を変更することもできます。
例えば、こんな感じ。

HYPERLINK(CONCAT("https://shop.googlemerchandisestore.com",ページ),ページ タイトル)

enter image description here

ページ(リンク可能)の列に表示されている文字列がページタイトルになっていますが、正しくリンクすることができます。

HYPERLINK関数の2番目のパラメータは表示する内容(ラベル)を表すため、文字を入れ替えて利用することもできるのですね。

ますます、Googleアナリティクスよりも便利だということが露呈してしまいました・・・。

まとめ:この機能はどんな人におすすめか

今回はHYPERLINK関数とCONCAT関数を用いて表中のURLをハイパーリンクにする方法をご紹介いたしました。
どちらの関数もGoogleアナリティクスでは難しい表示を実現するために使い勝手の良いものになっています。

特にGoogleデータポータルでレポート管理をしている方にとって、重宝するのではないでしょうか?
ぜひ使い方を覚えて、活用してみてください!

Gooagleデータポータルでハイパーリンクを設定する方法

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

Googleデータポータルだと表のハイパーリンクが設定されない・・・。そうお思いですか?実はGoogleアナリティクスのように表のURLから対象ページにリンクするだけでなくより複雑なリンク指定もできるんです。

この記事でまとめられていること

こんにちは。株式会社アピリッツでアナリストをしているssekiです。
最近GoogleデータポータルとGoogleアナリティクスを交互に見ることが多くなりました。

毅然としてGoogleアナリティクスを使い続ける理由はいくつかあるのですが、こと定点観測においてもデータポータルに比べてGoogleアナリティクスで便利だなぁと感じる点がひとつあります。

それは・・・、
「表から直接URLにリンクできる」ことです。

しかしながら、その考えは間違いでした。
実は、関数を使えばGoogleデータポータルでもハイパーリンクはできるんです!!!!

ということで、今回はGoogleデータポータルの表中でハイパーリンクを使用する方法についてご紹介いたします。

HYPERLINK関数を使ってハイパーリンクをつけよう

Googleデータポータルにハイパーリンク機能をつける関数、その名もHYPERLINK関数です。
名前そのまんまなので覚えやすいですね!

早速HYPERLINK関数の使い方を見てみましょう。以下はヘルプページからの引用です。

HYPERLINK(URL、リンクのラベル)
パラメータ:
 URL – URL として評価されるフィールドまたは式。
 リンクのラベル – フィールド、式、またはリテラル値。

あいかわらず、ヘルプページは簡潔すぎて意味がよくわからないですね・・・。
では、実際に簡単な例を通して使い方をご紹介いたします!

GoogleアナリティクスのようにURLをリンクさせる方法

Googleアナリティクスでは行動>サイトコンテンツ>すべてのページなどに表示されるディメンション「ページ」の表にハイパーリンクが設置されていますよね。
enter image description here

Googleデータポータルで同じような表を作成しても、デフォルトではハイパーリンクになりません。
そこで、HYPERLINK関数の出番です。計算フィールドで以下のディメンションを作成しましょう。

HYPERLINK(ページ,ページ)

URLの部分にページを指定、リンクのラベルの部分にもページを指定してみます。
そうすると、表ではこのように表示されますね。
enter image description here
ページ(リンク可能)と書いてある列が作成したディメンションです。
察しの良い方はお分かりでしょうが、これではリンクは機能しません。

見た目上同じなのになぜ?と思うかもしれませんが、URLに指定されている部分が”/”で始まっているので、リンク先のURLとして正しくない状態です。

そこで、URLの部分を次のように書き直してみましょう。

HYPERLINK(CONCAT("https://shop.googlemerchandisestore.com",ページ),ページ)

これで見た目は同じで、リンクが機能するようになりました。

えっ?初めてみる関数がある?
そうです。Googleアナリティクスデータを用いる場合、CONCAT関数を使って初めてHYPERLINK関数は機能するのです。

CONCAT関数で複数のものを結合させよう

CONCATはconcatenationの略で、日本語にすると「連結、結合」を意味します。
その名の通り、何かと何かをつなげる役割をする関数です。

使い方はいたって簡単。
CONCAT(A,B,C,・・・)のように、連結したい文字をパラメータに指定することで「ABC・・・」という文字列が生成されます。

先ほどHYPERLINK関数の利用例では、ドメイン部分をCONCAT関数によって前部分につけ、正しいURLに差し替えたというわけです。

クロスドメイン設定をしている場合はそのままでOK

ちなみに、先ほど誤用例として挙げた式ですが、全く使えないわけではありません。
実は、Googleアナリティクスでクロスドメイン設定をしている場合には、そのまま有効なリンクになるのです!

なぜなら、クロスドメイン設定時にはディメンション「ページ」がドメインから始まる正しいURLになるためです。

むしろ、この場合Googleアナリティクスの方でハイパーリンクが機能しない分、Googleデータポータルの方が優秀ですね。恐れ入りました。

※クロスドメイン設定とは、Googleアナリティクスでクロスドメイントラッキングを設定し、レポートビュー上でクロスドメインのフィルタをかけたものを指しています。

リンク表示名を入れ替えることも可能

HYPERLINK関数を用いれば、リンク表示名(ラベル)を変更することもできます。
例えば、こんな感じ。

HYPERLINK(CONCAT("https://shop.googlemerchandisestore.com",ページ),ページ タイトル)

enter image description here

ページ(リンク可能)の列に表示されている文字列がページタイトルになっていますが、正しくリンクすることができます。

HYPERLINK関数の2番目のパラメータは表示する内容(ラベル)を表すため、文字を入れ替えて利用することもできるのですね。

ますます、Googleアナリティクスよりも便利だということが露呈してしまいました・・・。

まとめ:この機能はどんな人におすすめか

今回はHYPERLINK関数とCONCAT関数を用いて表中のURLをハイパーリンクにする方法をご紹介いたしました。
どちらの関数もGoogleアナリティクスでは難しい表示を実現するために使い勝手の良いものになっています。

特にGoogleデータポータルでレポート管理をしている方にとって、重宝するのではないでしょうか?
ぜひ使い方を覚えて、活用してみてください!

【C#/Unity】コルーチン(Coroutine)とは何なのか

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

コルーチンについて理解を深めるためにざっくりと調べてまとめてみました。

はじめに

Unity、と言うよりC#など幾つかの言語には「コルーチン」という仕組みが備わっているのですが、実は今まであまり触れたことがありませんでした。
私のプロジェクトでは普段使う場面も無く、使わないまま苦手意識が芽生えてしまうのは非常にまずいので、調べてみてDoRubyの記事としてまとめることで理解を深めようと思います。

本記事で扱うコルーチンは、Unityの仕様として提供されているコルーチンを指します。

What is Coroutine

そもそもコルーチンがどんな仕組みなのか分かっていなかったので、そこから整理してみます。
Unityの公式マニュアルによると、

コルーチンとは実行を停止して Unity へ制御を戻し、ただし続行するときは停止したところから次のフレームで実行を継続することができる関数です。

とのこと。

つまりコルーチンは、「フレームを跨いで処理を中断・再開させることが出来る仕組みを持った、関数の亜種みたいなもの」という認識で良さそうです。

How to use Coroutine

マニュアルによると、IEnumerator型を戻り値とした関数を定義することで、その関数をコルーチンとして扱うことが出来るようです。
IEnumerator型はコレクションを扱うためのインターフェースなので、
using System.Collections; しておく必要があります。
定義したコルーチンを実行したい場合は、

StartCoroutine("コルーチン名")
StartCoroutine(IEnumerator型の変数)

のように呼び出すことで実行できます。(MonoBehaviourを継承する必要あり)

同様に、

StopCoroutine("コルーチン名")
StopCoroutine(Enumerator型の変数)

でコルーチンを停止(≠中断)できます。

使い方も分かったところで、適当にコルーチンを使うコードを書いてみました。

using System.Collections;
public class Test : MonoBehaviour{
    void Start(){
        IEnumerator coroutine = TestCoroutine();
        StartCoroutine(coroutine);
    }
    IEnumerator TestCoroutine(){
        Debug.Log("First.");
        yield return null;
        Debug.Log("Second.");
    }
}

StartCoroutineでコルーチン(TestCoroutine)が呼ばれると、コルーチンの中身が実行されてUnityのコンソールに「First.」と表示されます。
次の行に yield return null; と書いてあるのが、コルーチンの中断処理です。
これが呼ばれるとコルーチンは処理を中断するため、Unityは別の処理を行えるようになります。
そして、次のフレームになるとコルーチンの処理が再開され、先ほど中断した行の次の行から実行されます。
すなわち、「Second.」と表示されるわけです。
このように、yield return を挟むことで処理を一時中断し、次のフレームまで待機することが出来ます。

When to use Coroutine

コルーチンをどんな時に使えばいいのか考えてみます。

コルーチンを使って実現できるのは、「一連の処理を複数フレームに跨って実行させること」です。
例えば、横スクロールアクションゲームでありがちな「穴から敵が垂直に飛び出てきて攻撃して戻っていく」処理を例とすると、

①何らかの条件を満たすことで、敵が飛び出すフラグがオンになる
②60フレームかけて敵を上に移動させる
③5フレームかけて敵が攻撃するアニメーションを行う
④60フレームかけて敵を下に移動させる
⑤フラグを消す

といった5つのステップが考えられます。(フレーム数は適当です)
UnityではMonoBehaviourクラスを継承することでオーバーライドしたUpdate関数が毎フレーム呼び出されるので、Update関数を使ってフレーム処理を行うのが良さそうです。

ということでコルーチンを使わずに安直に実装したのがこちらです。

int frameCount = 0;
void Update(){
    if (moveFlag) {
        if (frameCount < 60) {
            // 上に移動させる処理
        } else if (frameCount < 65) {
            // 攻撃アニメーション処理
        } else if (frameCount < 125) {
            // 下に移動させる処理
        } else if (frameCount == 125) {
            moveFlag = false;
            frameCount = 0;
        }
        frameCount++;
    }
}

moveFlagがtrueの時だけUpdate関数の中でframeCountをインクリメントし、その値に応じて各処理を行います。
しかし、この書き方ではif文やswitch文による条件分岐が多くなりがちで、フレーム数をカウントするためだけの変数が必要になります。(しかもどこかのタイミングで0に戻す必要がある)
65や125といった数字も前の数字に依存するため、上に移動する処理を40フレームに変更しようとした場合、それに伴って65を45に、125を105に直す必要が出てきます。
(変更しやすいように各ステップに必要なフレーム数を予め定数にしておいた場合でも、
65の部分には60を表す定数と5を表す定数を加算する式が入ることになり、冗長になります)

そもそもこのようにUpdate関数を使う場合、moveFlagがfalseの間も毎フレームmoveFlagの判定を行ってしまい、無駄が多くなってしまいます。

そこで、同じ処理をコルーチンを使って書くことで、無駄を省いた上に分かりやすいコードを書くことが出来ます。

IEnumerator MoveAndAttack(){
    for (int i = 0; i < 60; i++) { // 60フレームだけ実行
        // 上に移動させる処理
        yield return null;
    }
    for (int i = 0; i < 5; i++) { // 5フレームだけ実行
        // 攻撃アニメーション処理
        yield return null;
    }
    for (int i = 0; i < 60; i++) { // 60フレームだけ実行
        // 下に移動させる処理
        yield return null;
    }
}

yield return null; にたどり着くまでの処理 = 1フレームで行われる処理なので、for文で60回実行させれば60フレーム分の処理が行えます。
このコルーチンを、前述のmoveFlagがtrueになるタイミングでStartCoroutineで呼び出してあげることによって、Update関数で無駄な判定を行うことなく必要な処理を必要な分だけ実行することが出来ます。

さいごに

初見ではややこしいと感じていたコルーチンでしたが、基本的な使い方が理解できれば便利さも実感できました。
ちなみに、フレームごとに処理を実行するだけでなく、指定した秒数ごとに実行させることも可能なようで、
その場合 yield return null; のところを yield return new WaitForSeconds(秒数); と変えることで実現できます。
また、StopCoroutineでコルーチンを止めた場合でも、全く同じコルーチンを再度StartCoroutineすると、StopCoroutineで停止した状態の続きから実行されてしまうようです。

悪い例

IEnumerator coroutine = ExampleCoroutine();
StartCoroutine(coroutine);

// 何らかの処理が挟まる

StopCoroutine(coroutine); // ExampleCoroutineが停止することを期待するが...
StartCoroutine(coroutine); // 完全に停止せず、続きから実行されてしまう

 
これはIEnumerator型の変数に、同じコルーチンを再度代入することで回避できます。
 

良い例

IEnumerator coroutine = ExampleCoroutine();
StartCoroutine(coroutine);

// 何らかの処理が挟まる

StopCoroutine(coroutine); 
coroutine = ExampleCoroutine();
StartCoroutine(coroutine); // coroutineの指す対象が変わり、正常に最初から開始

 
コルーチンは画面上のオブジェクトの動きをコントロールするのに非常に便利だと感じました。
これから少しずつ使い慣れて行きたいと思います。
機会があれば、今度は「カスタムコルーチン」についても記事を書いてみようと思います。

【C#/Unity】LINQを使ってみたらとてもスマートだった

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

コレクション操作をする際にLINQを使ってみたところ、素晴らしさに気付けたので紹介したいと思います。

はじめに

皆様は統合言語クエリ(Language INtegrated Query)すなわちLINQというものをご存知でしょうか?
私の場合、Unity業務に携わってC#を触り始めて1ヶ月ぐらいで先輩に教えてもらい、ようやく知った概念でした。
LINQを知らない・よく分からないといった方々にLINQの便利さを知ってもらえるように記事を書いてみました。

余談ですが、記事を書くにあたりおさらいをするために「LINQ」でググったのですが、福岡県を中心に活動しているアイドルグループが出てきました。
私はアイドルに詳しくないんですが、全員エンジニアのアイドルグループとかあったら個性強そうですね。というかありそう。

統合言語クエリって何?

簡単に言うと、SQLなどで馴染みの深いSELECTやWHEREといった単語を用いてコレクションのデータを操作できる仕組みです。

C#やVBなど公式にLINQに対応している言語以外にも、JavaやPHPなどメジャーな言語で有志によるLINQ風のライブラリが開発されています。

LINQを使うとどう変わるのか

Unityでゲーム(私の場合はソーシャルゲーム)を作っていると、「リストの中から特定の条件に合うものだけを表示する」という処理をすることが多々あります。

例えば、所持アイテムの中から戦闘中使用可能なアイテムだけを表示したり、全ユニットの中から進化可能なユニットだけを表示したり…。
一般的にユーザーが持つデータの数が増えれば増えるほど、条件による絞り込み表示の有用性は高くなると思います。

例として挙げた「所持アイテムの中から戦闘中使用可能なアイテムだけを表示する」処理を実装するときに一番単純明快なのは、以下のコードのようにforeachを使ってリストの要素を片っ端から見ていき、条件に一致するかどうか判断することだと思います。
(本来C#の命名規則はほとんどPascalCaseのようですが、最も書き慣れているので自分の所属しているプロジェクトの規則に則ることをお許しください。)

List<Item> usableItemList = new List<Item>();

foreach(var item in itemList){ // itemList = 全所持アイテムのリスト
    if (item.usableInBattle) { // アイテムが戦闘中使用可能かどうかのフラグを見る
        usableItemList.Add(item);
    }
}

foreachでリストの各要素を見ていき、if文で判定を行うという至ってシンプルな処理です。
本筋でないので省略しますが、もちろんこの後にusableItemListの中身を表示します。

このコードをLINQを使って書いてみると、こうなります。

List<Item> usableItemList = itemList.Where(item => item.usableInBattle);

たった1行で、先ほどの5行の処理を表せます。
Whereを使うことで、SQLのように条件を指定して絞り込みを行っているわけです。
5行から1行になったことは視覚的に大きな変化ですが、それ以外にもネストが減ることによる可読性の向上などの恩恵も受けられます。
ちなみに、LINQを使うためには using System.Linq; する必要があります。

メソッド構文とクエリ構文

先ほどの例では、Whereをメソッドのように使用してLINQを記述しました。
このような記法をメソッド構文と言い、それ以外にLINQにはクエリ構文というものがあります。

先ほどのコードをクエリ構文を用いて書く場合、

List<Item> usableItemList = item in itemList
                            where item.usableInBattle;

このようになります。

どちらを使っても同じ処理が出来るので、最終的には好みに依ると思うのですが、私はC#っぽさ(?)を重視してメソッド構文を使っています。
そもそも私のように生のSQLを書くことに慣れていない身としては、メソッド構文の方が圧倒的に書きやすく直感的でした。

LINQの主なメソッド

LINQには、もちろんWhere以外にもメソッドが用意されています。
どれも便利なので、私がよく使うものを簡単な例とともに載せておきます。
ちなみにLINQのメソッドはどれもIEnumerableクラスの拡張メソッドとして定義されています。

Where 条件指定して要素を抽出する

List<int> integerList = {0,1,2,3,4,5,6,7,8,9};
List<int> result = integerList.Where(value => value % 3 == 0); // {0, 3, 6, 9}

Select 要素に対して処理を行い、その結果を返す(射影)

List<int> integerList = {0,1,2,3,4,5,6,7,8,9};
List<int> result = integerList.Select(value => value * 10); // {0, 10, 20, 30, 40, 50, 60, 70, 80, 90}

Any 指定した条件を満たす要素が含まれているかどうか判定する

List<int> integerList = {0,1,2,3,4,5,6,7,8,9};
List<int> result = integerList.Any(value => value == 0); // true
List<int> result2 = integerList.Any(value => value > 9); // false

FirstOrDefault 指定した条件を満たす最初の要素を返す 無ければ要素の型の規定値を返す

List<int> integerList = {0,1,2,3,4,5,6,7,8,9};
List<int> result = integerList.FirstOrDefault(value => value > 5); // 6
List<int> result2 = integerList.FirstOrDefault(value => value < 0); // 0

ほぼ同じ処理をするメソッドにFirstがありますが、Firstは要素が見当たらなかった時に例外を発生させます。

OrderBy 昇順に並べ替えを行う / OrderByDescending 降順に並べ替えを行う

List<int> integerList = {3,0,2,8,6,9,4,7,1,5};
List<int> result = integerList.OrderBy(value => value); // {0,1,2,3,4,5,6,7,8,9}
List<Item> itemList = {item1, item2, item3, item4} 
// item1.price = 100, item2.price = 50, item3.price = 300, item4.price = 0 とする

List<Item> result2 = itemList.OrderByDescending(item => item.price); 
// {item3, item1, item2, item4}

まとめ

LINQを使うとコレクションに対する色々な操作が簡単に出来るよ!ということが伝わっていれば幸いです。
WhereやSelect、OrderByなど、戻り値がIEnumerable型のメソッドはメソッドチェーンして繋げることが出来るので、foreachやifで書くと長ったらしくて複雑な処理でも、LINQなら繋がっているメソッド名を見ることでなんとなく何をしているのかが分かりやすいのも強みだと思います。
他にも、LINQのメソッドの遅延実行によるメリットなど私もまだまだ理解が足りていない部分もありますが、基本的なメソッドと使い方を覚えるだけでforeachやifによるネストの回数がグッと減ると思います。

今回も長くなってしまいましたが、お付き合いありがとうございました。

【Unity】メニュー項目を増やして君だけの最強のUnityを作ろう!

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

MenuItem属性を使ってUnityのメニューをカスタマイズします。

はじめに

4月に入社し、配属先のプロジェクトのエンジニア業務を引き継いで早くも半年。
今までUnityで当たり前のように使っていた機能が、実は先輩方が作った機能だと知り驚きました。
Unityを使い始めたのも3月ぐらいなので、メニューに存在する項目は最初からあるものだと勘違いしていたのです。(無知)
…というより業務に関係する機能が多かったので、家でUnityを触っていても無いことに気付かなかったと言うのが正解です。

ということなので、今回はUnityのメニューに項目を追加して、便利なスクリプトをポチっと実行出来るようにしちゃおうという話です。

メニュー項目を追加する方法

さて、メニュー項目、つまりMenuItemの追加方法ですが、もちろんスクリプトで行います。
静的関数の定義時に[MenuItem]属性を付けることで、その関数がMenuItem(つまりメニュー項目)として登録されます。

[MenuItem("CustomMenu/MenuTest")]
static void MenuTest(){
    // なんかする
    Debug.Log("なんかした!");
}

こんな感じですね。
この場合、メニューの大項目に「CustomMenu」が追加され、その下に「MenuTest」という項目が追加されます。
クリックすると、MenuTest関数に記述した処理を実行できます。

enter image description here

[MenuItem("Window/MyWindow")] のように、既に存在する大項目の下に新たな項目を追加することも可能です。

ちょっと応用編

チェックマークをつける

Menu.SetChecked関数にメニュー項目のパスとtrue/falseを渡すことで、チェックマークが付けられます。
デバッグする際に切り替えたいゲーム全体に関わるフラグなどをここで変更できると楽ですね。
現在の状態を取得するのはMenu.GetChecked関数で行います。

[MenuItem("CustomMenu/Check")]
static void Check(){ 
    var path = "CustomMenu/Check";
    var checkFlag = Menu.GetChecked(path);   
    // 何らかのtrue/falseを切り替えたりする
    Menu.SetChecked(path, !checkFlag);
    Debug.Log("何らか is " + (!checkFlag).ToString() + "!");
}

ショートカットキーを設定する

追加した項目に対してショートカットキーを設定します。
MenuItemの引数に設定してるパスの最後に、「半角スペース」「修飾子キー」「文字」を入れることで設定できます。
以下の例では、#がShift、%がcommand(WindowsではCtrl)を表すので、
Shift + cmd + D で任意のスクリプトが実行可能になっています。

[MenuItem("CustomMenu/ShortCutTest #%d")]
static void ShortCutTest(){
    // なんかする
    Debug.Log("ShortCut!");
}
enter image description here

コンテキストメニューに追加する

右クリックや歯車マークのクリックなどで表示されるコンテキストメニューに、新たな項目を追加します。
選択中のアセットやコンポーネント等を取得し、それに対して処理を行うことも可能になります。

[MenuItem("CONTEXT/Component/ContextTest")]
static void ContextTest(MenuCommand menuCommand){
    // コンポーネントに対してなんかする
    Debug.Log(menuCommand.context);  
}

コンテキストメニューに項目を追加するには、MenuItem属性の引数であるパスの一番最初に CONTEXT/ を入れます。
上の例では、ComponentのコンテキストメニューにContextTestという項目を追加し、
MenuCommand型の引数を設定することで、その引数を使って取得したコンポーネントの情報を表示しています。
もちろん引数を取らなくてもメニュー項目を追加できます。
また、コンテキストメニューに対しても先ほどの方法と同じやり方でショートカットキーの設定が出来ますが、コンテキストメニューを開いている状態でないと有効になりません。

enter image description here

実際の使用例

私の所属しているプロジェクトでは、このMenuItem属性を使って、日々の作業を楽にしてくれるようなスクリプトをすぐに実行出来るようにしてあります。

AssetBundleのビルド
AssetBundleNameの設定
Unityエディタ上でゲーム実行時、AssetBundleのロードを行うかどうかのフラグ
選択したアセットを使っているプレハブやシーンを、GUIDをキーにして検索
JSONファイルからプレハブを作成

などなど、主にアセット管理の面で役に立つものが多いですね。先人に感謝。

さいごに

メニュー項目やコンテキストメニューの項目をカスタマイズすることで、自分のプロジェクトに適したUnityが構築できますね。

メニュー項目を増やして君だけの最強のUnityを作ろう!(タイトル回収)

BoxのAPIを使って見よう。(その5)~【実践編】Box-管理者-グループ管理-EXCELで管理-便利ツール

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

0.グループ情報のコントールには

グルーブ管理の権限が必要です。

EXCEL(VBA)から呼び出す方法は、過去記事を参照して下さい。

BoxのAPIを使って見よう。(その1)~概要と基礎情報
BoxのAPIを使って見よう。(その2)~実際にAPIでデータ取得
BoxのAPIを使って見よう。(その3)~VBAから呼び出す
BoxのAPIを使って見よう。(その4)~グループの関連情報を取得

1.その前に。

作成したVBAを動作させるとき「開発」画面を表示してイミディエイトに、リクエスト情報やレスポンス情報を表示させるのが面倒ですよね。
そのため、今回はひと手間として、テキストファイルでトレースログを取得してみます。

参考にさせて頂いたサイトは、こちらです。

エクスプローラーで表示した様子です。EXCELと同じ場所に出力します。
エクスプローラーで表示したとき

以下の要件を追加

・ファイルが無ければ、自動生成
 ⇒ 消してから確認しいって事ありますね?
・ファイルがロックされている場合は、エラーにしない。
 ⇒ エディタによっては開いた時にロックされてしまう場合があるので、その対策

ログ出力(プログラムサンプル)

Sub WLog(message As String)

    On Error GoTo ErrorHandler

    Dim LogPath As String
    LogPath = ThisWorkbook.Path & "\box_sample.log"

    Dim objFso As Object
    Set objFso = CreateObject("Scripting.FileSystemObject")

    If Dir(LogPath) = "" Then
        objFso.CreateTextFile (LogPath)
    End If

    With objFso.OpenTextFile(LogPath, ForAppending)

        .WriteLine Now & vbTab & message
        .Close

    End With

    Set objFso = Nothing

ErrorHandler:
End Sub

BOXのレスポンスJSONをログに出力するときの、ひと手間

いままでの記事でも紹介にさせて頂いて居る VBA-JSONを使用してログを整形して出力してみます。

Private Sub ResponseJSONLog(ByVal resJson As Variant)
    TraceLog.WLog "response JSON" + vbNewLine + JsonConverter.ConvertToJson(resJson, Whitespace:=4)
End Sub

上記の様な関数を用意しておいて、ログに出力してみます。
以下はメモ帳で表示して見たときです。

トレースログでJSONを整形して表示

2. 【本題】BOXでグループを操作

今回は以下のようなインターフェース画面を作成し簡単に管理できるような仕組みを構築してみます。

・グループ情報を取得して一覧表示
・新規にグループを追加
・既存グループに対して変更・削除

■初期画面
初期画面

■グループ一覧を取得してみよう。
グループ一覧を取得する場合

■グループ一覧を取得し実際に変更を加えてみよう。
各グループの追加・更新・削除を実施する場合

今回使用するBOX APIは

・Create Group
・Update Group
・Delete Group
・Get Enterprise Groups

Create Group

グループを新規に登録するAPI。※グループを管理する権限が必要です。

■接続方法(基本)

curl https://api.box.com/2.0/groups \
-H "Authorization: Bearer ACCESS_TOKEN" \
-d '{"name": "section-mmaster", "description": "section 2 group", "member_viewability_level": "admins_only"}' \
-X POST

■パラメタ内容

create params

■レスポンス

グループ情報が復帰します。

※fields に、必要なパラメタを指定しないと含まれません。
※同じグループ名が既に存在すると、、HTTP status codes が 409 conflict となります。

Update Group

グループを更新するAPI。※グループを管理する権限が必要です。

■接続方法(基本)

curl https://api.box.com/2.0/groups/GROUP_ID \
-H "Authorization: Bearer ACCESS_TOKEN" \
-d '{"name": "section-master"}' \
-X PUT

■パラメタ内容

update params

■レスポンス

グループ情報が復帰します。

※fields に、必要なパラメタを指定しないと含まれません。
※同じグループ名が既に存在すると、、HTTP status codes が 409 conflict となります。

Delete Group

グループを削除するAPI。※グループを管理する権限が必要です。

■接続方法(基本)

curl https://api.box.com/2.0/groups/GROUP_ID \
-H "Authorization: Bearer ACCESS_TOKEN" \
-X DELETE

■パラメタ内容

delete params

■レスポンス
正常にDELETEされた場合には、HTTP status codes が 204 no_content となります。

Get Enterprise Groups

企業のすべてのグループを取得するAPI。※グループを管理する権限が必要です。

前回の記事 を参考にしてください。

3.サンプル

サンプルを作ってみましたので、試したい方はご自由にどうぞ。
サンプルEXCELをダウンロード
※VBAのコードは、現時点では非公開です。
※操作可能なグループ数は25に制限されています。

サンプルデータのダウンロード画面

4.まとめ

次回は
グループにユーザとフォルダを追加・更新・削除する機能を作ってみたいと思います。

※アピリッツではBoxの販売パートナーもしてるようです。

代理店販売してますよ。

お問い合わせは、こちら へ

フリー素材を知っておこう

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

 ゲームを作るにはたくさんの材料が必要である。「こんなゲームを作りたい!」という気持ちや構想はもちろん大事だが、それを形作るための音楽や画像がなければ実現できない。

フリー素材を探す

 「音楽 フリー素材」のような感じで検索すればすぐに見つかるので素材を探すこと自体は非常に簡単。敵グラフィックだろうが、攻撃アニメーションだろうが使い切れないぐらいには見つかる。そして利用規約を守れば誰でも自分のゲームに組み込める。そう、誰でも。

誰でも使えることに気をつける

 誰でも使えるということは、すなわち他人のゲームと同じ素材を使う可能性が高いことを意味している。もし有名なゲームが既に使っていれば、フリー素材という事情を知らないユーザーからは最悪パクリだと思われて、叩かれるという悲劇が起こったりする。
 一方で、自分がプレイした他人のゲームに欲しい素材があったら、そのゲームのクレジットから素材を逆に辿る事も出来る。(パクったわけじゃないんだからね!)

素材の管理に気をつける

 配布サイト毎にダウンロードした素材は管理しておかないと、後で困ったことになる。例えば利用規約が変わって商用利用禁止になったとき、自分のゲームがまだ未公開で商用利用するつもりだったならそれらの素材はゲームから抜く必要のある場合がある。(サイト運営者に問い合わせるのが確実。)
 また、配布サイトが閉鎖してしまう場合に備えて利用規約はどこかに記録しておくといい。

有償素材も考える

 他人と素材が被るのを気にするなら、有償の素材を使うことも一つの手。今や数百~数千円程度でまとまった素材が買える時代なので、よほどお金に困っていなければこちらを選ぶとグッと気が楽になるし捗ったりする。

余談

 フリー素材がどんなものがあるか知っていると、「実は大ブームを起こした有名ゲームが
フリー素材を使っている」なんてことに気づくこともあったりする。グラフィックは自製で、決定音のような誰も気にしない部分はフリー素材を使うというのも基本的で有効な手段だとよく分かる。

配列同士のinclude

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

プログラムを書いていて少し迷ったことがあったので書いていきます。

arr_1 = [1, 2, 4, 5, 5, 8, 9]
arr_2 = [2, 5, 8]

arr_1の中のものからいくつか選んだものがarr_2に入っている」という状態で、
arr_2arr_1の中身の組み合わせで成り立っているかを調べる必要がありました。


rubyでは配列同士は「-」演算子で差分を取れるのを知っていたので

(arr_2 - arr_1).empty?

このようにチェックをしようとしたのですがこのやり方だと…

arr_1 = [1, 2, 4, 5, 5, 8, 9]
arr_2 = [2, 2, 2, 5]

arr_2 - arr_1
=> []

こんな感じに重複した値がすべて消し去られてしまいます。
自分の場合は重複したものも個別に判定する必要があったのでこれではまずいです。

どうせ便利なメソッドがあるだろうと探しましたが見当たらなかったので自分で作ることにしました。

class Array

  def include_of_array(arr)
    temp = self.dup
    arr.each do |value|
       idx = temp.index(value)
       # 見つからなければ終了
       return false if idx.blank?
       temp.delete_at(idx)
     end    
     return true
  end  

end
arr_1 = [1, 2, 4, 5, 5, 8, 9]
arr_2 = [2, 2, 2, 5]

arr_1.include_of_array(arr_2)
=> false

arr_1.include_of_array([1, 5, 5, 9])
=> true

一件落着です。

Googleデータポータルで1日あたりのトランザクション数を確認する方法

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

今回はGoogleデータポータルの対象期間における1日あたりの平均トランザクション数を計算する方法についてご紹介いたします。DATE_DIFF関数やTODATE関数といったヘルプページだけではわかりにくい関数の実用TIPSとしてご活用いただければ幸いです。

この記事でまとめられていること

こんにちは。株式会社アピリッツでアナリストをしているssekiです。
Googleデータポータルには数多くの便利な関数があるのですが、使い方がなかなかわかりにくいのが正直なところ。
僕もヘルプページをにらみながら日々勉強しています。

今回ご紹介するのは、日付に関する関数です。実例を交えながらご紹介いたします。
例えば、こんな事例ってよくありませんか?

Aさん「このECサイトの目標は日平均○○個売上を上げることなんだけど、これってデータポータルで定点観測できないかな?」

僕「えっ、それって1日あたりのトランザクション数を計測するってことですよね。うまいこと計測日数を計算できればいけそうだけど・・・。ちょっと調べてみます。」

・・・というわけで、日数を計算する方法を探してみた僕ですが、なんとか解決できたのでご紹介いたします。

データポータルで計測日数を計算するにはDATE_DIFF関数とTODATE関数を使おう

ある程度調べていくと、Googleデータポータルにおいて日付がどのように扱われているか、次のようなことがわかりました。

  1. 日付はYYYYMMDD形式で指定する必要がある。
  2. TODATE関数を用いることで、日付データを他の形式に変換できる。
  3. DATE_DIFF関数を用いることで、2つの日付間を計算できる。

ここまでわかってくると、後はDATE_DIFF関数とTODATE関数を組み合わせれば何とかなりそうですね。
早速、それぞれの関数の使い方を調べてみることにしました。

DATE_DIFF関数の使い方

まずは、2つの日付間を計算するための関数、DATE_DIFF関数の使い方からまとめていきます。
Googleデータポータルのヘルプには、このように記載されています。

説明:XとYの日数の違いを返します(X-Y)。
構文:DATE_DIFF(X,Y)

かなりシンプルですね。しかし、ここで四苦八苦しました。
例えば、次のように仮の日付を当てはめてみてもエラーが出てしまいます。

DATE_DIFF('20171011','20171010')

また、シンプルに「日付」のディメンションを入れても、当たり前ながらダメでした。

DATE_DIFF(日付,日付)

どうすれば計算できるの???と小一時間悩み続けていたのですが・・・、

「データポータルで使える日付の形式が決まっているなら、もしかして日付の出力形式をDATE_DIFF関数が使えるようにしないといけないんじゃないか?」とやっと気づいたのでした。

つまり、TODATE関数の出番ってことですね!

TODATE関数の使い方

というわけで、日付データの形式を他の形式に換える関数、TODATE関数についてまとめていきます。
またもや、ヘルプページはシンプルにこう書いてありました。

説明:UTCの指定された形式で日時の項目を返します。
構文:TODATE(X,入力形式,出力形式)

UTCというのは、世界各地の標準時を決めるときの基準「世界標準時」のことです。
パソコンのタイムゾーン設定でよく見かけますね。

TODATE関数のXの部分は、指標やディメンションなどのデータを入れます。
そのデータの入力形式を次に指定し、変換したい形式をその次の出力形式に当てはめるようです。


ここで使える入力形式と出力形式には制限があり、次の文字列が使用できます。

【入力形式】
・「‘BASIC’」 YYYY/MM/DD-HH:MM:SS
・「‘DEFAULT_DASH’」 YYYY-MM-DD[HH:MM:SS[.uuuuuu]]
・「‘DEFAULT_SLASH’」 rmat YYYY/MM/DD [HH:MM:SS[.uuuuuu]]
・「‘DEFAULT_DECIMAL’」  YYYYMMDD [HH:MM:SS[.uuuuuu]]
・「‘RFC_1123’」 表示例:Sat, 24 May 2008 20:09:47 GMT
・「‘RFC_3339’」 表示例: 2008-05-24T20:09:47Z
・「‘SECONDS’」 エポックからの秒数
・「‘MILLIS’」 エポックからのミリ秒数
・「‘MICROS’」 エポックからのマイクロ秒数
・「‘NANOS’」 エポックからのナノ秒数
・「‘JULIAN_DATE’」 エポックからの日数
・「‘DECIMAL_DATE’」 ’DEFAULT_DECIMAL’ と同じ
文字列として有効な strptime 形式

【出力形式】
“%Y” 年
“%m” 月
“%d” 日
“%W” 週番号
“%w” 曜日
“%H” 時
“%M” 分

詳細についてはヘルプページをご参照ください。

ひとまず、この形式に合わせて日付をコントロールすればよさそうだということがわかりました。

対象期間の始点・終点間の日数を計算する方法

実際に、DATE_DIFF関数に合う形式で日付を修正してみましょう。
指標「日付」、入力形式「’DEFAULT_DASH’」、出力形式「’%Y-%m-%d’」で計算してみるとDATE_DIFFに合う形になるようです。

下のコードを計算フィールドに入力することで、対象期間の始点・終点間の日数を計算できます。

DATE_DIFF(TODATE(日付,'DEFAULT_DASH','%Y-%m-%d'),TODATE(日付,'DEFAULT_DASH','%Y-%m-%d'))

ここで、やってみて不思議だったのですが、DATE_DIFFのXとYの位置に同じTODATE構文を入れるだけで自動的に始点と終点を見分けてくれていました。

おそらく、Googleアナリティクスデータがそのような形式になっているのでしょう。

とにもかくにも、目的の第一段階であった対象期間の日数をカウントすることができました。
あとひといきです。

1日あたりのトランザクション数を計算する方法

次に、1日あたりのトランザクション数を計算してみましょう。
指標「トランザクション数」を先ほど作ったDATE_DIFF構文で割るだけです。

トランザクション数/DATE_DIFF(TODATE(日付,'DEFAULT_DASH','%Y-%m-%d'),TODATE(日付,'DEFAULT_DASH','%Y-%m-%d'))

簡単ですね。
式が複雑になってわかりづらいという方は、さきほどのDATE_DIFF構文を別の名前で保存して使ってもよさそうです。

まとめ:この機能はどんな人におすすめか

というわけで、無事に1日あたりのトランザクション数を計算することができました。

今回の日数計算の関数利用は様々な利用が考えられそうですね。
例えば、
・1日あたりのユーザー数を調べる
・リニューアル日(特定の日)から何日経っているかを調べる

など、少し上げただけでも、多くのマーケッターにとって実用的な機能な気がします。

ぜひ、記事を参考にいろいろ試してみてください!

flattenで多次元を1次元に

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

今回は、rubyのflattenメソッドの紹介をしていきます。

flattenメソッドとは

flattenは、配列とハッシュに使用することができます。
配列に対してflattenメソッドを使用すると、2次元配列や3次元配列などの階層の深い配列を1次元配列に再構築してくれます。

[ [1, 2], [3, 4], [5, 6] ].flatten
=> [1, 2, 3, 4, 5, 6]

[ [ [1, 2], [3, 4] ], [ [5, 6], [7, 8] ] ].flatten
=> [1, 2, 3, 4, 5, 6, 7, 8]

ハッシュに対してflattenメソッドを実行すると、ハッシュのキーと値の一次元配列が返ります。

{ "apple" => 3, "grape" => 2, "peach" => 5 }.flatten
=> ["apple", 3, "grape", 2, "peach", 5]

しかし、ハッシュの値がハッシュである場合は値のハッシュは変換されません。

{ "red" => { "apple" => 3 }, "purple" => { "grape" => 2 } }.flatten
=> ["red", {"apple"=>3}, "purple", {"grape"=>2}]

mapメソッドとの組み合わせて使用する

ここで紹介しているflattenメソッドですが、mapメソッドと組み合わせて使用することで階層の深い関連データを1次元の配列で取得することができます。
例をProject Programmer Workモデルで説明します。目的は、Projectに関係のあるProgrammerのWorkの一覧を取得することです。
enter image description here

以下のコードがProjectに関係のあるProgrammerのWork一覧を取得するコードです。

project = Project.find(1)
project.programmers.map(&:works).flatten

このコードが何をしているのか説明していきます。
まず、project.programmersでprojectに紐づくprogrammerを参照しています。

project.programmers
=> [#<Programmer id: 1, name: "taro">, #<Programmer id: 2, name: "jiro">]

次にproject.programmers.map(&:works)でprogrammerに紐づくworkを参照しています。ただし、2次元配列で返ってきます。

project.programmers.map(&:works)
=> [[#<Work id:1, status: "new">, #<Work id:2, status: "fin">], [#<Work id:3, status: "fin">, #<Work id:4, status: "fin">]]

最後にproject.programmers.map(&:works).flattenで2次元配列を1次元配列に再構築しています。

project.programmers.map(&:works).flatten
=> [#<Work id: 1, status: "new">, #<Work id: 2, status: "fin">, #<Work id: 3, status: "fin">, #<Work id: 4, status: "fin">]

まとめ

eachなどのループを使用すれば、この記事で紹介したように階層の深いデータを取得することができますが、mapとflattenを使用すれば1行で納めることができます。自分の経験上、関連データの関連データを一覧で取得する機会があまりないですが、rubocopで行数を抑えたいときに使えるかと思います。

超高速通勤に注意!?未来に行く方法

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

またもや通勤がサブテーマですが、今回は社会人なら常に気を配りたい”時間”にまつわるお話です。

人によって流れる時間が違う!

時刻ピッタリに電車が発車したり、5分でも遅刻は遅刻であったり、期限を守らないと大変なことになったりと、現代日本はとても時間に正確な社会ではないでしょうか。
しかし、こんなにも社会の軸とされている”時間”というものが、そもそも人によって流れる速さが違うことをご存知でしょうか?

これは、超有名な物理学者アインシュタイン博士が発表した、「相対性理論」(正確には特殊相対性理論)によって導かれる結論です。
この理論によれば、運動している速度が速ければ速いほど、その人の流れる時間が遅くなるらしいのです。
例えば、ロケットに乗って1時間ぐらい超高速の宇宙お散歩をして帰ってくると、地球では1日経過していた、みたいなタイムスリップのようなことが起こるのです。
enter image description here
一見現実味がないようなこの理論ですが、実はすでに色々なものに応用されており、その身近な代表例がGPS時計です。
GPS時計は人工衛星を使って、いつでも正確な時刻を時計に送っているのですが、人工衛星は地球の周りを時速1万キロで回り続けているため、
速度による時間の遅れが無視できなくなってしまいます。そこで、内部で相対性理論に基づいた補正を行い、正確な時刻を出しているのです。

未来に行く!?

さて、すでに例で出したように、超高速のロケットに乗って、宇宙旅行を楽しんで帰ってくることで、地球ではロケットの中以上の時間が流れているため、結果として未来に行くことが出来ます。
ここでは、実際にどれぐらいの速度で走れば実感できるほど未来に行けるのかを計算してみたいと思います。
事前の計算でかなり速度が必要なことが分かったので、いきなりぶっ飛ばします。

①秒速269813.2122km
秒速約27万キロです。1秒あれば大体地球を7周ぐらいします。
この超高速ロケットに乗っている人の1秒は、地球の人の約2.3秒に相当します!
この速度を出せれば、このロケットの中の時間は、地球時間のおよそ半分の速度で流れることになります。
つまり、このロケットの中で5年過ごすと、地球では約10年が経過します。劇的ではないですが、まあまあなタイムスリップになるのではないでしょうか。

②秒速299792.158207542km
秒速約30万キロです。1秒で大体地球を7周半します。
この超高速ロケットの中の1秒間は、地球上の人のなんと707秒に相当します!
つまり、このロケットの中の時間は地球上の700分の1の遅さになるのです。
ここで1年過ごすだけで、地球では700年もの歳月が流れ、あっという間に浦島太郎状態です。

**考察**
この速さを実現するには1年のような長い時間をかけてロケットを加速し続ける必要があります。(ちなみに1時間ほどでこの速さまで加速しようとすると、体に8500G(体重の8500倍の力)が掛かります。注意してください)
従って、
①数年単位で加速し続けられるエンジンと燃料
②数年間人間が生活し続けられる環境
③途中の惑星などの影響をよけられるようなシステム
これらが実現できれば、この未来への旅行も現実味を帯びてくるのではないかと思います。未来に行ける世界は意外と近くまで来ているかもしれませんね。

補足:上の例で出している速度は、光の速さ(秒速299792.458km)を基準に、①その90%の速さ、②その99.9999%の速さ、で計算したものになります。
光の速度に近づくにつれて急激に時間が遅くなることが分かります。
(ちなみに、現在のロケットは、秒速数十km程度であるようなので、今のロケットに乗っても実感できるほど未来に行くには、まだまだ速さが足りません。)

過去には戻れるの?

未来に行くことが出来ることは分かりました。しかし、過去に行くにはどうすればよいのでしょうか。
相対性理論において、物体の速度をどんどん上げていくと、光の速度を超えたところで時間が逆転し、過去に進むようになります。
しかし、現在の物理学では、質量をもつ物体の速度は光の速度を超えることはできないとされています。
そのため、残念ながら現状では過去への行き方は分かりません。未来に行くときには、もう戻れないことに注意してくださいね。

終わりに

いかがでしょうか。通勤速度を追い求めるあまり、未来に行ってしまって結局遅刻する、のようなことが無い安全運転の通勤をしたいですね。

Rails×chart-js-railsで統計を表示する 1.

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

2は多分ある。

 ナポリタンの食品サンプルがデスクに増えました。

発端

「Railsでデータベース使って色々やってるんだから、それ使って簡単なグラフとか見れたら面白そうだな…..。Railsでグラフを表示する用のgem、探してみよう」
「今回はchart-js-railsを使ってみることにしよう。有名どころでも使われているみたいだし、このgem自体はjavascriptのライブラリのchart.jsをRailsで使う為に引っ張って来てるだけのものみたいだし」

使用方法

「Gemfileに一行付け足して、bundle installして、使う為には、application.jsに

//= require Chart.min

 を書き込む、と。
 それだけで、他にコマンドとかを打つ必要は全く無し。
 さて、まずは適当に新しいページ作ってchart.jsのサンプルページとか見ながら試してみるか……。
 新しくそれ用のコントローラ作ってroute.rbにルーティング付け加えて、それからviewを作成して」

Controller
graph_viewers_controller.rb

class GraphViewersController < ApplicationController
  def sample
  end
end

View
sample.html.erb

<canvas id="myChart" width="900" height="400"></canvas>
<script>
var ctx = document.getElementById("myChart").getContext('2d');
var myChart = new Chart(ctx, {
    type: 'bar',
    data: {
        labels: [1,2,3,4,5],
        datasets: [{
            label: "サンプル",
            data: [1,2,3,4,5],
            backgroundColor: 'rgba(255, 99, 132, 1.0)',
            borderColor: 'rgba(255, 50, 132, 1.0)',
            fill: false
        }]
    },
    options: {
        title:  {
          display: true,
          text: "タイトル"
        }
    }
});
</script>

bar graph test
「お、出来た。
で、これにデータやラベルを変数として組み込むには、配列部分を弄れば良さそうだな」

Controller

def sample
  pivotes = PiVote.pluck(:name, :vote_num)
  @labels = pivotes.map(&:first)
  @datas = pivotes.map(&:second)
  #@labels = ["◯っ◯◯","π","パイナップル", "ニシンパイ", "パイルドライバー"]
  #@datas = [9999, 3141, 2, 8, 1000]
end

View
「ラベルとデータ部分をそれぞれ弄って、と」

    data: {
        //文字列の入った配列やハッシュ等はこうしないと正しく表示されない
        labels: <%= @labels.to_json.html_safe %>,
        datasets: [{
            label: "投票数",
            data: <%= @datas %>,
            backgroundColor: 'rgba(255, 99, 132, 1.0)',
            borderColor: 'rgba(255, 50, 132, 1.0)',
            fill: false
        }]
    },

enter image description here
「おお、でけたでけた。
 一つ二つでも弄れば円グラフとか散布図とかも色々作れそうだし、オプション設定も色々あるし、夢が広がるぅ」

実際の使用感覚

 まあ、そんな感じで使ってみました。実際にデータが視覚化されると、特徴とかが分かりやすくなりますし、そこからプロジェクト等に対する改善策も見やすくなってくると思います。
 このchart-js-railsは実際、chart.jsをRailsで使えるようにしただけのgemみたいなものですが、Javascriptを余り深く知らない自分にとっては、Javascriptの勉強にもなって中々面白い所もあります。
 今もそれに関して主に弄っていて、グラフの色分けや、散布図の点をクリックしたらそのデータに関する画面に遷移したりとか、実際色んな事が出来るようになってきて、実際夢が広がるぅ、感じです。
 でも、一つ、難点として挙げるのならば、データベースから取ってきた値などを、javascriptにrubyとして組み込んでいくので、コードがとても汚くなりがちです。
 散布図を色分けやらしたい、なんて考えた時は、データを与える辺りのコードが以下のようになってしまいました。

  datasets: [
    <% @labels.each_with_index do |label, i| %>
    {
      label: "<%= label.html_safe %>",
      //色の設定。数に応じて変化。
      borderColor: "<%= "##{(16 * i / @labels.length).to_s(16)}f#{(16 * (@labels.length - (i + 1)) / @labels.length).to_s(16)}fff"%>",
      backgroundColor: "<%= "##{(16 * i / @labels.length).to_s(16)}5#{(16 * (@labels.length - (i + 1)) / @labels.length).to_s(16)}5ff"%>",
      pointRadius: <%= 4 + i %>,
      pointStyle: "<%= ['circle', 'triangle', 'rect', 'star', 'cross'][i].html_safe%>",
      showLine: false,
      data: [
      //データの配列。データの一つはx,y座標を格納した配列。
        <% @datas_array[i].each do |data| %>
        {
          x: <%= data[0] %>,
          y: <%= data[1] %>
        },
        <% end %>
      ]
    },
    <% end %>
  ]

enter image description here
まあ、もっと良いやり方はあると思うので、もっと色々試してみようとは思いますが。

指を動かして脳を活性化!?手と脳の関係とは?

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

手や指を動かすことによって脳にどのような影響があるのかについて調べてみました。

はじめに

1か月の更新になります。今回はプログラミングに関してではなく前々からちょっと興味があった「手と脳の関係」について調べてみたのでそれを記事にしたいと思います。

1.手と脳の関係

いきなりですがこちらの画像をご覧ください。
enter image description here
photo by Mike

なかなかにグロテスクな画像ですがこれはカナダの脳神経外科医であるワイルダー・グレイヴス・ペンフィールド(1891~1976)が考案した「ホムンクルス人形」と呼ばれるものです。手や唇は大きく、逆に足や胴、腕などはとても細いのが見て取れます。とても歪な容姿をしていますがこれはヒトの大脳皮質と体の部位の感覚の関係を部位の大きさで示したものです。大脳皮質とは大脳の表面にある神経細胞の集団で、100億以上の神経細胞がいくつかの層をなして配列しています。大脳皮質には触覚や温痛覚などの感覚を脳にインプットする感覚野や運動を行い外にアウトプットする運動野などがあります。
このホムンクルス人形を立体から平面図にした写真が下になります。
enter image description here
photo by Beth Scupham
これまた少し不気味な画像ですが、感覚野(Somatosensory Strip)と運動野(Motor Strip)に分かれてそれぞれ対応した部位の大きさでどれくらいの割合を占めているかを表しています。
運動野でも感覚野でも手や口の部分が大きな割合を占めているのがわかります。
この部位の大きさが大きいほど感覚が敏感であるとされ、その部位を使うことで脳のたくさんの部分を使っていることになります。

2.脳を活性化させよう

 さて、先ほどまでの内容で手と脳が密接に関係していることを確認してきました。手をたくさん使うことで脳細胞に刺激を与え、脳の活性化へと繋がります。
 手を使うと、手を使わない時に比べて脳の血流量が10%程度上がるという研究結果があるそうです。脳の血流量が低いと神経細胞が死滅してしまい、二度と復活することはありません。神経細胞が減っていくと物忘れが激しくなったり、ボケを引き起こしたりすることがあります。認知症の予防で手を使った体操や、先ほどの画像で手の次に大きな割合を占めている口を動かすことを薦められているのはこういった所からきていると思われます。
 しかし、ただ手を使えばいいという問題ではありません。普段の日常生活で行うような「物を掴む」「箸を使う」といった行為ではあまり刺激にはなりません。「外国の人が箸を初めて使った」といった場合ならとても刺激になると思いますが、何も考えなくても行えるぐらい体に染みついた行為は脳の活性化にはあまり効果がありません。
 脳を活性化させるのに効果的なのは考えながら手を動かすことです。特に、ピアノなどの楽器演奏や、キーボードタイピングなどは手を動かしながら頭も使うことができるのでとても良いとされています。ピアノは音の強弱やリズムなどを楽譜を追いながら頭で考えて指をたくさん動かす為、脳へたくさんの刺激が伝わります。しかし、そのためにやった事のない楽器を1から始めるのはかなり大変です。それに比べてキーボードタイピングは、打ちたい内容を考えながらキーをタイプするだけなので比較的簡単に誰でも始められます。そのほかにも普段行うことを利き手と逆の手で行うだけでも脳には刺激になりますのでちょっとだけ意識して生活してみるといいかもしれません。

おわりに

 いかがだったでしょうか。自分はルービックキューブが好きでよくやっているのですが、「指を使うと頭が良くなる」と聞いたことがあり、気になっていてちょうどいい機会だと思い調べてみました。結果的には全体的に頭が良くなるというより記憶力の部分が良くなるという感じでしたが、ちゃんとした理由を知ることができました。
 ルービックキューブやタイピングをしていて「脳が刺激されてるなー」とか、「今、脳の血流量が10%上がったな…」と感じたことは1度もありませんが、将来の記憶力や健康の為に普段から意識して指を動かす事が大事だと思うので、みなさんも是非意識して指を動かして脳をたくさん刺激しましょう!

Gitで間違えてブランチをマージしてしまった時の対処法

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

はじめに

こんにちは、motsukaです。
今回は、Gitでマージしてはいけないブランチをマージした時の対処法を紹介したいと思います。

状況

現在開発しているreleaseブランチと新ステージ実装をしているdevelopmentブランチがある状態で、
新ステージの実装が終わったのでdevelopmentブランチをreleaseブランチにマージしてpushしました。
ですが、まだその新ステージはマージしてはいけないものでした。

では、マージコミットを取り除くにはどうしたらいいでしょうか?

解決策

Gitにはコミットを取り消す方法は幾つか有ります。
有名なのが、resetとrevertですね。
今回はrevertを使ってマージコミットをrevertしてみます。
まずgit logでマージしたコミットのcommitIDをコピーします。

次に git revert commitID と打ってみます。すると

fatal: Commit commitID is a merge but no -m option was given.

と言うエラーが出ます。

これはマージコミットには -m をつけてくださいと言うエラーです。
-m オプションは、mainlineを指定するオプションです。
マージコミットをrevertした後の状態とは、二つのブランチの状態のどちらかになりますよね?
この二つのブランチのどちらに状態を戻すかを指定しなければなりません。
-m の後ろには1か2を指定することができて、1がマージされたブランチ、2はマージしてくるブランチです。
ここでは1がreleaseブランチ、2がdevelopmentブランチですね。

では、 git revert -m 1 commitID と打ちます。

すると、revertコミットがされたことが確認できます。
これでコミットを取り除くことができました。

ですが、これだともう一度新ステージをマージしようとした時、マージができません。
すでにマージされているというエラーが出てしまいます。
その解決策として、developmentブランチにreleaseブランチをマージします。
そして先ほど取り除いたコミットをまたrevertします。
そうするとまたマージした時にエラーが出ることがなくなります。

まとめ

gitで間違えた時の対処法は各自調べておきましょう。

コミュニケーション技術

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

コミュニケーションは「伝わらないが大前提」 今回は少しでも伝えるためのコミュニケーション技術についてのお話です。

なぜ伝わらないのか?

「話し手が考えていること」と「聞き手が考えていること」
果たして同じでしょうか?
それぞれの生い立ち、言語、文化、常識、年齢etc
人間100人いたら100種類の人生があるはずです。
話し手と聞き手の思考がまったく同じなんてことはほぼないと言っていいでしょう。
そのため、伝えたい内容と伝わる内容は根本的に同じではなくなり伝わらないといったことが起きます。

ラポールを意識しよう

人間関係は信頼関係です。
人間関係の深さは必ずしも関わった時間に比例しないというのはなんとなく理解してもらえるかと思います。
それでは何で決まるのか?相手との信頼関係です。
そしてなんと信頼関係の構築は技術で習得できます。
具体的にはどうするのか?
「ラポールを確立させる!」ことが重要です。

ラポールってなに?

互いに信頼し合い、安心して感情の交流を行うことのできる関係が成立している、心的融和状態を指します。
「親近感」や「信頼感」といった言葉が近いと思います。

実際にどうやるの?

これには様々な方法があります。
主に「同調」「傾聴」「自己開示」といった非言語行動を使用し、
コミュニケーションをすることで生まれると云われています。
今回は「同調」について少し掘り下げます。

同調の手法

ミラーリング

主に身体の使い方を合わせる同調手法になります。
合わせるのは姿勢、座り方、手振り、態度、表情などです。
ピシッとした姿勢に対してだらっとした姿勢、
笑った顔に対して真顔などされると話しづらいですよね。
相手と合わせることで各段に話しやすくなります。
ただし、手振りなどはやりすぎると気付かれて逆に不愉快にさせてしまうこともあるので注意が必要です。

ペーシング

これは相手の話し方や状態、呼吸などのペースを合わせることです。
合わせるのは声の調子、高低、大小、リズム、スピードなどです。
さらに感情の起伏や場の空気などに注意し、合わせてあげることが出来ると
相手と一体感が生まれてきて、同時に相手は安心して話すことが出来るようになると思います。

バックトラッキング

いわゆる「オウム返し」です。
事実を反復、感情を反復、確認を入れるなどになります。
相手の話をちゃんと聞いていることを示すこと、相手に自分が発した言葉を再確認してもらうことが目的です。そのため、一語一句同じでなくてはならないというわけではありません。
相手の使った表現に適していれば相手は自分の話がよく理解され受け入れられているという感覚を持ちます。

まとめ

少しでもコミュニケーションを上手くするために、
今回は代表的な同調の手法として
・ミラーリング
・ペーシング
・バックトラッキング

以上3点をご紹介しました。
良ければぜひ試してみてください。
他にもラポールを確立させるための手法は沢山あります。
今後また機会がありましたら他の手法についてもご紹介します。

ActiveRecordとデータベースについて知っておきたいこと

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

Ruby on RailsのActiveRecordをぼーっと使っていると、メモリにあるデータやdbにあるデータの整合性が取れなくなってバグを生んでしまう事があるという話です。

ActiveRecordが好きなのでたびたびActiveRecordの話をしますが、今回はうっかりやってしまいがちなDB上のデータとメモリ上のデータの不整合との話です。知らないと失敗しがちなので

railsは5.1.1を使っていますが、あまりバージョンには関係なく、rails固有というわけでもなさそうな話です。

うっかりしてしまった!

プレイヤーが複数持つデッキの中から、一つだけメインデッキを選べるような実装がしたいとします。

class Player < ApplicationRecord
  has_many :decks
  has_one :selected_deck, -> { where(selected_flg: true) }, class_name: "Deck"

  def select_main_deck(deck)
    transaction do
      self.decks.each{ |deck| deck.update!(selected_flg: false) }
      deck.update!(selected_flg: true)
    end
  end
end

素直に読むと、一度すべてを非選択状態にし、選択したいデッキを選択しているようです。
色々問題はありますが、この select_main_deck が同時に使われることがないと仮定しても、
今メインデッキとして選択されているデッキをまた選択した場合、全てのデッキが非選択状態になってしまうのです。

※ ふつうeachではなくupdate_allを使うところですが、update_allにはまた別の落とし穴があり、本筋からそれるここではeachを使っています。

どうしてこうなってしまうのか

一瞬何が悪かったのかわかりにくいですが、

ひとつめ: DBで変更があったとしてもメモリ上のデータは更新されない

下の例では first_player が指すものと another_first_player が指すものはメモリ上では別の場所におかれており
ActiveRecodr::Baseは勝手に変更を取ってきてはくれません。

first_player = Player.first # #<Player id: 1, name: "あああああ">
another_first_player = Player.first
another_first_player.update!(name: "いいいいい")
p first_player 
=>  #<Player id: 1, name: "あああああ"> # DB上のデータとは違う

another_first_player からupdateされたものはanother_first_playerには反映されますが、たとえ同じデータを参照していても、オブジェクトとして別のものである first_player にはその変更は反映されません。

これは絶対知っておいたほうがいいことです。
読み込んで表示するだけならば問題ないですが、今のデータをもとに更新を行う場合は、DBからデータを読み込んでから更新するまでの間に他で変更されないよう、きちんと排他制御をしましょう(後述します)。

ふたつめ: 変更がなければUPDATEは走らない

ActiveRecord:::Base は DBからデータをひいてきてメモリ上のオブジェクトが作られたあと、updateなりsaveなりを使ったとしても、変更がなければDB上のデータを更新しようとしません

player = Player.first # #<Player id: 1, name: "あああああ">
player.update!(name: "あああああ")
# UPDATEは走らない

これで何が起こっているかはっきりしてきたかと思います。

def select_main_deck(deck)
  transaction do
    # 全てのデッキのselected_flgがfalseになる
    self.decks.each{ |deck| deck.update!(selected_flg: false) }
    # DB上ではselected_flgがfalseだが、メモリ上ではtrueのまま変わらないのでUPDATEされない
    deck.update!(selected_flg: true)
  end
end

こういったバグ、特に他人や昔の自分がかいたコードを読んだ場合かなり見つけにくいですね。

上に挙げた

  • 同じデータを違うオブジェクトから参照する
  • データの取得と更新を行う

の2つはバグの温床になりがちなので気をつけたいところです。

さて、この悲しい事態を避けるにはどうすればよかったのか考えてみましょう。

どうすればよかったのか

そもそも構造を変える

この場合なら、playersテーブルに selected_deck_id というカラムを追加して、そこで選択中のデッキを持っておいたほうが筋がいい気がします。
選択中のデッキを切り替えるときに複数のレコードを更新しなくて済み、データ不整合を起こすことがなくなります。

class Player < ApplicationRecord
  has_many :decks
  belongs_to :selected_deck, class_name: "Deck"

  def select_main_deck(deck)
    self.update!(selected_deck: deck)
  end
end

この場合に限らず、何らかの操作をしたとき、更新する部分があまり多くならないよう考えておくとバグが起こりにくいですね。

ロックをとる

どうしても上に挙げた方法が使えないときは、きちんと不整合を防ぐ仕組みが必要です。
例えばロックというDBからデータを読み込んでから更新するまでの間にデータが更新されないようにするDBの仕組みがあります。

たとえば所持金を増やしたいとき、知らないうちにDBのデータが更新されてしまうと困ったりしますよね。

class Player
  def add_money(val)
    # 今の所持金を200とすると
    added_money = self.money + 100
    # ここで、DB上でのplayerの所持金が300になる
    self.update!(money: added_money)
    # 本当は400なのに300に...
  end
end

「所持金を増やす」間に他のところでデータが書き換えられないようにしないといけません。
たとえば rails では、以下のように with_lock メソッドを使って書くと、with_lockに与えたブロックの中の処理が終わるまで別の場所でDB上のデータが更新できなくなります(こういった、一つのレコードに対しての更新を制御するものは行ロックと呼ばれています)。

class Player
  def add_money(val)
    with_lock do
      added_money = self.money + 100
      self.update!(money: added_money)
    end
  end
end

さらに気をつけたいのは、複数のレコードに対してロックを取る場合です。
一度ロックを取ってしまうと処理が終わるまでは他ではデータの更新ができないため、順番次第ではお互い更新が終わるまで待ち状態になる、いわゆ
るデッドロックという状態になってしまうことがあります。「食事する哲学者の問題」がわかりやすい例ですね。

デッドロックに陥らないためには、たとえば必ずidが大きい方からロックを取るなど、一意な順番でロックを取っていく必要があります。

class Player < ApplicationRecord
  has_many :decks
  belongs_to :selected_deck, class_name: "Deck"

  def pay_for(target_player, val)
    if self.id < target_player.id
      self.with_lock do
        target_player.with_lock do
           target_player.update!(money: target_player.money + val)
           self.update!(money: self - val)
         end
       end
     elsif self.id > target_player.id
      target_player.with_lock do
        self.with_lock do
           target_player.update!(money: target_player.money + val)
           self.update!(money: self - val)
        end
      end
    end
  end
end

まとめ

基本的にこういったデータの不整合が起きやすいのは、 同じDBのレコードを指すオブジェクトが複数できるとき です。

まずそういう状況に気づくこと、気づいたら意図しない場所での更新が起きないようにすること、そもそも広い範囲での更新が起きないよう考え直すことを意識していきたいですね。

シェルスクリプトと戦う〜その1〜

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

はじめに

サーバ内部でバックアップを取るなど、特定の決まった作業をコマンドで打ち続けるとだんだん面倒になることがあります。こういう時は自動で作業するよう組みたくなるものです。
また、定期的ではないものの、よくやる一連の作業をいちいち入力するのも面倒ですね。なんとかしたいものです。
そこでシェルスクリプトというものが登場します。このシェルに普段入力しているコマンドを記述し、実行すれば、記述したコマンドを順々に実行してくれます。
今回はこのシェルに関しての話で触り始めて学んだことや思ったことを紹介していきます。

※ほとんどコマンドを触ったことのない人は迂闊に触らないほうがいいと思います。また、場合によってはファイルの消失や上書き等取り返しのつかないことが発生するコマンドもあるので、なるべく実行環境とは別の、構造が似た様な環境を構築してそちらでテストを行ってから実行環境に反映させることを推奨します。(コピー、削除等)

まずは書く

実行環境:CentOS5.9

helloWorld.sh

#!/bin/bash
echo ‘Hello!’
echo ‘Shell world!’

ただechoで2行表示するだけの簡単なシェルです。bash以外にsh等もあるようですが、環境によって動作が異なる部分があるようなのと、bashで見かけるソースが多かったので、こちらで記載していきます。

結果

enter image description here
確かに表示されました。
これで普段実行されているコマンドを書いていけばいいですね!

そこまで甘くない

シェルと同じディレクトリで実行する想定で記述したと仮定します。
実行は同じディレクトリからと、別のディレクトリからの両方を行います。

passCheck.sh

#!/bin/bash
echo '=== pass check ==='
pwd
echo '=== move ==='
cd Desktop
pwd
echo '=== end ==='

結果

enter image description here
pwdで確認したパスが変化しています。「shファイルと同じディレクトリに移動して実行しないと動かない」ルールで行えば、一応問題ありません(ミスをおこしかねないのでお勧めしません)が、今回のようにDesktopフォルダに移動してから実行すると、パスが変化するため、エラーが発生してしまいます。
今回はpwdで自身の位置を確認し、cdで移動するコマンドだけなので影響はありませんが、場合によっては実行されなかったり、想定外の事態が起こったりします。

さて画像から分かるかもしれませんが、実行時にディレクトリの移動を行っても、シェルの終了時に元の位置に戻されます。

対応としては、
・少なくとも最初のcdは絶対パスで記述する
あくまで「少なくとも」です。なるべくフォルダ移動の際は絶対パスの方がいいのではと思いますし、コピーコマンド等も、こだわりがなければその方がいい気がします。

cdのパス指定ですが、シェルと同じ位置に行く場合はdirname $0を利用した方法があります。ただ、実行およびソースの書き方によっては、期待する処理と結果が異なります。(参考:dirname $0 でスクリプト置いてあるディレクトリにならない 1年以上前の記事ですが同様の動作を確認)
$0ですが、これは初期値ではシェル自身のパスが格納されています。(〜.shの形)

終わりに

今回は触り始めになるだろう部分を紹介してみました。まだ紹介していませんが、if文や変数なども使用できます。これらを利用して処理を分けたり弾いたりできますが、後に簡単なもので紹介しようと思います。


関連記事:
dirname $0 でスクリプト置いてあるディレクトリにならない
シェルスクリプトと戦う〜その2〜

キャラクターでストーリーを動かす①

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

ゲーム内イベントのシナリオを書いていると、登場させるキャラクターの人選によって行き詰ってしまうことがあります。 手っ取り早く起承転結に導いてくれる優等生を探しましょう。

はじめに

お話を書いているうちに、展開が詰まってどうしてもストーリーが動かなくなってしまった…という経験がある方は少なくないと思います。
こうなってしまうと、にっちもさっちもいかなくなってしまいますね。
泥沼を回避するためにも、物語を動かすキャラクターに協力してもらいましょう。

今回のお話は、私自身が現在シナリオを担当しているタイトルを例に、一からキャラクターを作るのではなく、既にゲーム内に登場しているキャラクターを数人ピックアップしてイベントシナリオを書く、という想定です。

登場人物を決める①

私が現在担当しているタイトルでは、ほぼ毎回イベントの主役となるキャラクターが事前に決まっています。そのキャラクターを軸にイベントのシナリオを考えていきます。
登場人物が多いタイトルなので、イベントごとに登場させるキャラクターを変えて、色々なキャラクターの魅力を伝えていきたい…と思ってはいるのですが、キャラクター同士にも相性というものがあります。直近のイベントに登場していない、なおかつ主役となるキャラクターと相性の良いキャラクターを選ぶことを心がけていますが、中々うまくいきません。
(そもそも第一に考えなくてはならないのは、シナリオをうまく回すことではなく、ユーザーの方々に満足していただくことなので…。)

今回の例では、季節にちなんだイベントで「十五夜」をテーマにします。
まずはキャラクターを選んでプロットを組んでみないことには始まらないので、数人ピックアップします。

登場人物を決める②

enter image description here
※画像はイメージです。

主役として登場させることが事前に決定していた①の明るい女の子を軸に、過去のイベントに登場していないキャラクターの中から4人ほど選びました。
左から、
②中二病の子
③王子様のような子
④十五夜から「兎」を連想して、過去にバニーの恰好をしたことがある子(うさぎさん)
⑤生真面目だけど少し子供っぽいところがある子、です。

実際にこのメンバーでイベントシナリオを書こうとして数日間筆が進まず悩み続けてしまったので、後日練り直すことになります。
この中には、話の起点となる誰か……つまり「起承転結」の「起」の部分になってくれるキャラクターがいなかったためです。(私の力不足でもありますが…。)

登場人物を決める③

締め切りも迫って来たので焦り、試しに登場人物の変更を試みます。
ゲーム画面の仕様上、一度に5人以上のイラストを表示してしまうと会話画面が詰まりすぎてしまいますので、一つのイベントに登場させるキャラクターは、今のところ4~5人まで、としています。(演出上、全員を一斉に表示させることが多いので。)
ですので、前項の画像③の王子様のような子、④のうさぎさんの出番は次回のイベント以降に回し、新たに下の画像③のスピリチュアルさんを投入しました。

enter image description here
※画像はイメージです。

このスピリチュアルさんが起承転結をうまくコントロールし、ストーリーはするすると動いていくことになります。

「登場人物を決める②」の時点では、自由奔放に動き回るキャラクターは多いものの、場をおさめて新たな展開につなげる役割のキャラクターがいませんでした。
スピリチュアルさんは物静かで意味深なことを話す上に、カードによる占いを得意とするキャラクターなので、「占い」を起点にし、物語を動かしていくことが出来るのです。
このように、何か特別なことを得意とするキャラクターは特に、起点にしやすいです。

ようやく登場人物が決まりました。
続きは次回です。

最近人気な記事