Ruby の関数型プログラミングの特徴

Enigmo Advent Calendar 2018 の7日目の記事です。

概要

Enigmo の Steven です。 プログラミング言語に対して興味を持ってますので、今日は Ruby について話したいと思います。

Rubyオブジェクト指向だと言ったら、反対する人は多分いないと思いますが、関数型言語の特徴も持ってると言ったら、ピンとこない人はそれほど少なくはないかと思います。 それでも、Ruby プログラマーでしたら、関数型言語から受け継がれたそういう機能はおそらく毎日使っています。 そういう機能がなかったら、Ruby は世界中で使われてる現在の言語にならなかったかもしれません。

この記事ではその機能を説明して、Ruby の理解と関数型言語に関する知識が少し深まる機会になれればと思います。

Ruby の特徴について

Wikipedia によると、Ruby は 10個以上のプログラミング言語から影響を受けて作られました。 その中には PythonC++Smalltalk などのオブジェクト指向プログラミング言語は複数ありますが、その中には Lisp もあります。

Lisp というのは現在、一つの言語というより、言語のファミリーですが、そのファミリーの言語は特徴的な文法を用いてることで有名でしょう。 ただし、Ruby では括弧は Lisp と比べて極めて少ないですし、文法も全然違うので、最初は関係を疑うかと思いますが、言語の根本的なところで Lisp の一部の特徴を確認できます。

Lisp から引き継がれたもの

条件式の場合、Ruby の世界では nil と false は偽として解釈されます。それ以外の値はすべて真になります。 PHP などの言語と比べて、極めて簡単でわかりやすいルールですが、その特徴は Lisp からそのまま引き継がれました。 それに関しては Clojurejvm 上の Lisp)は Ruby と全く一緒です。Common Lisp では false という値はないですが、それ以外は一緒です。

もう一つの特徴としては、Lisp と同じく Ruby には文はなく、すべては式です。 C などの言語では if文、関数の定義などは文であって、値として扱えないのですが、Ruby ではどんなものも式であって、値として扱えます。 if then else endは該当のブランチの値を返しますし、多くの場合はそんなに役に立たないと思いますが、def foo; endはメソッド名をシンボルとして返します。

以上は Lisp 由来の特徴の一部ですが、引き継がれたものの中で一番影響が大きかったのは、関数型プログラミングだと言えます。

Ruby の関数の扱い

どんな段階で特定の言語が関数型言語になるかというのは定かではなくて、判断しにくい時がありますが、Ruby ではやはりオブジェクト指向の面が一番強いので、関数型言語だと言えません。 ただし、Ruby ではある程度関数型プログラミングが可能だと主張しても、誤りではないでしょう。

Ruby関数型プログラミングができるのは Ruby の関数の扱いのおかげです。 厳密に言うと、Ruby には関数はなく、すべてはメソッドですが、説明がより簡潔になるよう、以下の説明では両方が同じだと一旦みなしてください。

第一級関数(first-class functions)

第一級関数を提供する言語では関数を単なる値として扱えます。他の関数に引数として渡すこともできれば、関数から返すのはもちろん、変数に保存することもできます。 Ruby では既存のメソッドを値として扱うにはまずメソッドをMethodProcオブジェクトとして抽出する必要がありますが、それができれば、そのオブジェクトを他のオブジェクトと何の違いもなく自由に扱えます。

def add(a, b)
  a + b
end

def apply(fn, *args)
  fn.call(*args)
end

add = method(:add)

apply(add, 22, 44)
# => 66

無名関数(anonymous functions)

無名関数はその名前の通り、名前のない関数を表しています。それだけです。 名前を与える必要がなくなると、整数と文字列と同じく、関数はただのリテラルになります。 Ruby ではproclambdaで無名な関数オブジェクトをもちろん生成できますが、ブロックも無名関数の一種だと言えます。

my_proc = proc { puts 1 }
my_lambda = lambda { puts 2 }

def with_my_block
  yield 3
end

with_my_block { |x| puts x }

クロージャ(closures)

関数を値として使える言語では、クロージャは、関数の内側から、関数定義時に関数の外側にしか存在しなかった変数名(グローバル以外にも)を参照することを可能にします。 Ruby でもクロージャのサポートを確認できます。

def foo(n)
  o = n * 2
  lambda { |p| o + p } # o は lambda の引数から受けられず、foo のローカル変数を指しています
end

bar = foo(10)
bar.call(5)
# => 25

無名関数とクロージャは時々一緒くたにされることがありますが、違うものです。 無名関数がないが、クロージャがある言語と、その逆の言語を想像できます。

高階関数(higher-order functions)

高階関数は以上のものと違って、機能ではなく関数の一種類の名前です。 高階関数というのは、他の関数を引数として受けるか、関数を返り値として返すか、それともその両方を行う関数を表しています。 どちらかが欠けてると、多少不便になるので、以上で紹介した機能をベースに、高階関数を可能にするプログラミング言語は多いです。 Ruby では高階関数は多いですが、一番始めに頭に浮かぶのはEnumerableのメソッドです。

multiplier = 5
# map という関数はブロックを関数として受け取ってます
[1, 2, 3].map { |n| n * multiplier }
# => [5, 10, 15]

したがって、Ruby でプログラミングをしているのであれば、おそらく毎日、ある程度の関数型プログラミングをしているということになります。

関数型プログラミングのいいところ

Enumerableのメソッドを好む人は Ruby プログラマーのほぼ全員だと思いますが、そのようなプログラミングがなぜいいのかをもっと具体的に説明しましょう。

  • 関数を組み合わせることで量の少ないコードでもかなりな処理を行えます
  • 高階関数になると、再利用できる関数は多く、アルゴリズムをそれぞれの関数の組み合わせとして実装できます
  • 命令型プログラミングとオブジェクト指向プログラミングと比べて、ステートを扱うことが少ないので、ステートによるバグがより少ない

特徴は他にありますが、まとめて言いますと、関数型言語では数学により近い形でプログラムを実装すると言えます。

関数型プログラミング向けの gem

Ruby で以上のように関数型プログラミングができますが、Ruby はあくまでもオブジェクト指向の言語なので、それなりの限界があります。 高階関数をライブラリーに追加することは簡単ですが、HaskellOCaml のような関数型言語に近づかせるにはかなりの努力が要ります。 それも度を越えると、RubyRuby じゃなくなって、デメリットの方が大きくなることがあるかと思います。

それでも場合によってはほかの関数型言語にある機能を Ruby に持ってくるメリットがありますので、以下ではいくつかの機能とそれを提供する gem を紹介します。

永続データ構造

永続データ構造では既存の値を変更することは不可能で、もとの値から新しい値を生成することしかできません。 新しい値の生成時に既存のデータ構造と一部の構造が共有されるので、HashArrayなどをまるごとコピーするより早いです。

一見では追加の制限しかかけられてないと見えますが、新しい値をしか生成できないというのは逆に保障でもあって、変更してはならないデータ構造を気づかずに変更してしまうというバグが完全になくなります。 並行計算ではスレッドの間でデータ交換を行う時に伴う心配もなくなるほか、たまに見かける#dup#freezeのメソッド呼び出しも不要になります。

関数型言語に限る機能ではなく、それぞれのオブジェクト指向言語にも普及しつつある機能であって、Ruby でもこれからStringが完全にイミュータブルになることから見て、Ruby では将来的にそのデータ構造が公式的に導入されるかもしれません。

パターンマッチング

Ruby では値を比べるには==はもちろん、case when endで使われる===もあります。 ただし、そのメソッドで値が同じであるかどうかを簡単に判断できても、データの一部だけ(いわゆるデータのパターン)を簡単に比べるのはもっと難しいです。 その問題を解決するため、データの比較をより汎用的に行えるように、パターンマッチングという機能が存在します。

パターンマッチングでは、比較したいパターンを定義することで、データがパターンにマッチした場合、もしくはマッチしなかった場合の処理を指定できます。 パターンがいくら複雑でも定義可能なので、複雑なデータ構造の比較時に特に役に立ちます。 例のパターンとして以下のはどうでしょうか?

  • 値が配列で、最初の要素がハッシュである必要があるが、ほかの要素は気にしません
  • ハッシュに:foo:barのキーが必須ですが、追加で他のキーがあって問題にしません
  • :fooに対する値は 10 の整数である必要があります

Ruby でパターンマッチングを可能にする gem の中には以下のがあります。

モナド

Arrayに対して使える#map#flat_mapメソッドは人気でしょう。 ただし、Arrayでの使い方が言語を問わず一番普及しているとはいえ、#map#flat_mapは配列限定のメソッドではありません。

#mapはコレクションの各要素に対して任意な関数を適用して、その結果をもとの要素に置き換える処理を行います。 Arrayの場合、ここでいうコレクションは配列ですが、#mapを提供できるコレクションは他にもいろいろあります。 配列は 0 以上の要素を含むとして、Maybeは 0 か一つの要素を含むコレクションです。 フューチャーでしたら、将来的に一つの要素を含むことになりますが、現在はまだないかもしれません。 全然違うものに見えるかもしれませんが、以上のものはすべて要素を含むコレクションとして一緒です。 そのコレクションでも#mapは含まれてる要素に対して任意の関数を適用する処理を行うことになります。

#flat_mapはコレクションの各要素に、その要素を引数にしながら、同じ種類のコレクションを返す関数を、適用する処理を行います。 関数から返された各コレクションを、コレクションの種類によって、連結したり、そのまま返したりします。 #flat_mapと一緒に使われる時、最終的の結果を変更しない関数も使用可能な場合は(以下は#pureと呼ぶ)、#map#pure#flat_mapだけを使って実装可能です(配列の場合はpure = ->(x) { [x] })。 #pure#flat_mapを提供するデータタイプ(コレクション)はモナドと呼ばれています。

#map#flat_mapのいいところの中には以下のがあります。

  • 高階関数なので、再利用性は高いですが、その中でも#map#flat_mapはかなり根本的な関数なので、それをベースに、何のコレクションにも対応する関数を、実装できます。
  • #map#flat_mapは通常の実装で全域写像(total function)なので、渡された関数も全域写像であれば、どんなコレクションに対して適用されても、エラーになりません(Maybeのコレクションで#map#flat_mapを使えば、nil のチェックは完全に不要になります)。

モナドを本格的に使うには協力な型を提供する静的型付けな言語が大体必要になりますが(Haskell など)、簡単なユースケースなら、Ruby でも以下のライブラリーなどでモナドを使えます。

言語とライブラリーによって#map#flat_mapは違う名前になったりします。他によく使われる名前としては fmap(map)と bind(flat_map)はあります。

終わりに

プログラミング言語の世界では、最近の一つの流れとして、今まで関数型言語にしかなかった機能が少しずつ他の言語でも採用されるようになってます。 Ruby ではかなり以前から高階関数を使えますが、もっと最近な例として Java 8 があります。新しい言語なら、Swift と Rust もあります。 当分はその流れはおそらく止まらないので、これからも関数型プログラミングの機能がどんどん普及して、もしかすると Ruby の方にも新しい機能が現れるかもしれません。 そうなれば、今以上にも Ruby関数型プログラミングのいいところを活かせるのでしょう。

Redashがバージョンアップ(v4)して便利になったこと

エニグモ データ活用推進室 @kmt47 です。 この記事はEnigmo Advent Calendar 2018の6日目です。

概要

redashがv4にバージョンしました。 redash v4の新機能を紹介します。 上手に新機能を使って、redash作成を効率化していきましょう!

クエリ(レポート(表、グラフ))

数値の表示形式(フォーマット)をredashで設定できるようになった(便利度:★★★★★(MAX))

これまでは、SQLで行っていた(面倒な)表示形式の変換を、redashの設定で可能となりました。

これまでは、文字列変換(CONVERT)や、文字置換(REPLACE)を駆使して金額の12,000.00の「.00」の除去や、 YoYなどにパーセンテージ(%)の付加を行って表示形式を整形していましたが、v4ではredashの設定で可能です。

手順

・「Table」画面の左下にある「Edit Visualization」をクリック   ※ 自分が編集できる(自分が作ったor編集権限のある)クエリの場合に「Edit Visualization」が表示されます。  ⇒ 「Visualization Editor」が表示されます

・表示形式を変更したい項目の「Number format」を変更(数値項目のデフォルト値は「0,0.00」)

「0.0」に変更することで、小数点以下が除去され「12,000」のように表示されます。 ※ 表示形式を変えると、右側の表示の形式も瞬時に変更されますので、確認ができます。

YoYのようなパーセントの項目の場合は、「0.0%」に変えることで、「123.4%」のように表示されます。 ※ パーセンテージの場合の元の値は、100を掛ける必要はない(100掛けちゃダメ)

表示形式は、以下のURLを参照してください。 http://numeraljs.com/

項目を非表示にできるようになった(便利度:★★★★)

v4以前はSQLから項目を削除する必要があったが、SQLには残したまま、redash上で非表示にできるようなった。

表(Table)では表示させたくないが、グラフでは使いたい、といった場合も、クエリを分ける必要がなく、 1つのクエリで実現できるようになった。(開発生産性、運用保守性が格段にあがりますね!)

手順

・「Table」画面の左下にある「Edit Visualization」をクリック

・非表示にしたい項目の項目名にあるチェックボックスからチェックを外す

項目の表示順を変更できるようになった(便利度:★★)

項目の表示順をSQLの変更なしに、redashで変更できるようになりました。

手順

・「Table」画面の左下にある「Edit Visualization」をクリック

・順番を変更したい項目をクリックして、移動したい場所に持っていきます。

項目の表示を右寄せ、中央寄せ(?)、右寄せの設定が可能になった(便利度:★★)

v4以前は、数値でも、文字列でも、なんでも右寄せでしたが(なので特に数値は読みずらかった)、 項目ごとに変更できるようになりました。

手順

・「Table」画面の左下にある「Edit Visualization」をクリック

・項目名の下にあるボタンで切り替える(説明が雑ですが、見れば分かると思います。)

表に表示する件数を変更できるようになった(便利度:★★★)

これまでは15件(?)だった表示件数を変更できるようになりました。

手順

・「Table」画面の左下にある「Edit Visualization」をクリック

・「Visuralization Name」の下の「Grid」をクリック

・「items per page」で表示したい件数を選択  ※ いまいま5, 10, 15, 20, 25から選択可能   (1か月の日数分表示するために50とか欲しいなぁ)

ダッシュボード

クエリの追加・削除・レイアウト変更の方法が変更

ダッシュボードにクエリ(表やグラフ)を追加・削除・レイアウト変更する方法が変更になりました。

手順

ダッシュボードの右上の「・・・」をクリック

・「Edit」をクリック

クエリの追加

・画面右下の「Add Widget」をクリック

・追加したクエリを選択  ※ v4以前で選択できた、表示サイズ(幅)は、ここでは選択できなくなりました。    追加した後にサイズを調整できるように変更となりました。

クエリの削除

・削除したいクエリの右上の「×」をクリック

クエリのレイアウト変更

・移動したいクエリを選択して、マウスで移動したい場所まで移動

クエリのサイズのバリエーションが増えた(便利度:★★★)

v4以前は、通常(画面の幅の半分)と2倍(画面の幅の全部)のみ選択できました。 v4になって、新たに画面の1/3のサイズにすることができるようになりました。

手順

ダッシュボードの右上の「・・・」をクリック

・「Edit」をクリック

・サイズを変更したいクエリを選んで、クエリの枠にカーソルを置くと、カーソルが「⇔」に変わるので、クリックしてサイズを変更します。(説明が雑ですが、やれば分かります。)

クエリの高さが変更できるようになった(便利度:★★★★)

上記と似てますが、幅だけではなく、v4から新たに高さを自由に変更することができるようになりました。

手順

ダッシュボードの右上の「・・・」をクリック

・「Edit」をクリック

・高さを変更したいクエリを選んで、クエリの枠にカーソルを置くと、カーソルが「↕」に変わるので、クリックしてサイズを変更します。(説明が雑ですが、やれば分かります。)

GoogleスプレッドシートとGoogleAppsScript(GAS) はじめの一歩(非エンジニア向け)

エニグモ データ活用推進室 @kmt47 です。 この記事はEnigmo Advent Calendar 2018の5日目です。

自己紹介

2018年4月にエニグモに入社しました。 社会人経験、エンジニア経験は、かれこれ20年を超えました。 あえて分類するとデータベースエンジニアになるでしょうか。 SIer時代は、ご多分に漏れず、PG, PL, PMなど経験しましたが… その頃は「DBといえばOracle」という時代でしたので、Oracleは7から9iくらいまでかなりやりました。 そこからネット系の事業会社を転々と。

この記事の目的

タイトルに「はじめの一歩」とあるように、はじめてGoogle SpreadsheetでGoogle Apps Script(GAS)を利用する方向けの内容です。 どちらかというと非エンジニア向けの内容です。

新しいことを習得しようとしたときに、誰もが最初は初心者です。いろいろな壁にぶつかると思います。何時間も悩んだ挙句、気づいてみたらとても些細なことだった、なんてことも一度や二度ではないでしょう。 ただ、ある程度コツが掴めてくると、そこからは順調に事が進むようにもなりますよね。(エラーが出たときの対処の方法や、分からないことが出てきた時の調べ方のコツが身についてくるんだと思います。)

ただ、コツを掴む前に挫折してしまう人もいるでしょう。 そもそも、最初の一歩すら踏み出せない方もいるでしょう。

本記事は、そんな一歩を踏み出すための「何となくできそうかも」を感じてもらうことを目的としています。なので、最小限の手順で、最小限のプログラムとし、難しい解説は省いています。

まずは「はじめの一歩」を踏み出していただければ幸いです。

GASとは?

Google Apps Script(GAS)とは、Google社が提供するスクリプト言語です。以上。

GASで出来ること

GASでは、GoogleスプレッドシートGoogleカレンダーなどGoogle社が提供するサービスに対して操作(処理)することができます。まぁ、いろいろできますが、それは追々分かっていけばよいと思います。

本題

目次

  1. Google Spreadsheetを開く
  2. スクリプトエディタを開く
  3. GASを実行する
  4. スプレッドシートの値を取得する
  5. スプレッドシートに値を出力する

Google Spreadsheetを開く

Googleドライブから「新規」>「Googleスプレッドシート」>「空のスプレッドシート」を選択

空のスプレッドシートを開くことができました!!(GAS以前の話ですね)

スクリプトエディタを開く

「ツール」>「スクリプトエディタ」を選択

別のタブにスクリプトエディタが開きます。

プログラムを書くエディタらしき画面ができていましたね。(パチパチ)

GASを実行する

2行目に以下を記載して、「▶」ボタンを選択

Logger.log("Hello, World!");

Logger.logは、引数の値をログに出力する関数です。

「表示」>「ログ」を選択、ログ画面が表示

おめでとうございます。あなたの作成したGASが見事に実行されました!(パチパチパチ)

スプレッドシートの値を取得する

A1のセルの値を読み込んで、ログに出力してみましょう。

ここから少しプログラミングっぽくなってきますよ。

function myFunction() {
  //アクティブのシートを取得
  var sheet = SpreadsheetApp.getActiveSheet();
  
  //セルA1を変数rangeに取得する
  var range = sheet.getRange('A1');
  
  //rangeの値をログに出力する
  Logger.log(range.getValue());
}

SpreadsheetApp.getActiveSheet()で対象のシートを変数(sheet)に格納します。 getActiveSheet()では、アクティブ(いま選択されている)シートが選択されます。 getSheetByName(シート名)では、シート名からシートを選択できます。

次に対象のセルを変数(range)に格納します。 sheet.getRange('A1')のように、セルのアドレスを指定する方法と、 sheet.getRange(1, 1)のように、セルの行番号と列番号を指定する方法があります。 sheet.getRange(行番号, 列番号)の順で指定します。

Logger.log(range.getValue())では、指定したセルの値をログに出力しています。

A1のセルの値がログに出力されました。

スプレッドシートに値を出力する

次は、スプレッドシートに値を出力するプログラムです。

function myFunction() {
  //アクティブのシートを取得
  var sheet = SpreadsheetApp.getActiveSheet();
  
  //セルA3を変数rangeに取得する
  var range = sheet.getRange('A3');
  
  //A3に"Hello, GAS!"を入力
  range.setValue("Hello, GAS!");
}

スプレッドシートの値を取得する」と同様に SpreadsheetApp.getActiveSheet()でシートを変数(sheet)に格納し、 sheet.getRange('A3')で対象のセルを変数(range)に格納します。

そして、range.setValue("Hello, GAS!")で対象のセルに値を入力します。

A3のセルに値を出力することができました。

まとめ

データを読み込んで、書き出すというプログラミングの基本を通して、GASによるGoogleスプレッドシートの操作について見てきました。

「何となくできそうかも」と感じていただけたでしょうか?

本格的にGASを利用するとなると、関数、変数、データ型、オブジェクト、メソッド、配列などなど、知っておく必要のあることは、いろいろあります。

ただ、いっぺんに全て理解する必要はありません。必要なことから少しずつ理解を進めていけば大丈夫です。

私はその過程で以下のサイトを大変参考にさせていただきました。 例題をもとに分かりやすく解説されていますので、是非参考にしていただければと思います。

「いつも隣にITのお仕事」 https://tonari-it.com/

【保存版】初心者向け実務で使えるGoogle Apps Script完全マニュアル https://tonari-it.com/google-apps-script-manual/

それでは、GASへの「はじめの一歩」を踏み出していただければ幸いです。

React/Redux約三年間書き続けたので知見を共有します

Enigmo Advent Calendar 2018の4日目の記事です。

この記事の目的

Enigmoが運営しているBUYMAでは古代から運用しているjQueryの他に、2016年頃から一部ページのフロントエンドをReact/Reduxで構築しています。 私自身もEnigmoに入社してからの約三年間でReact/Reduxアプリケーションの開発に多数携わってきましたので、そこで培った知見を共有したいと思います。

React/Reduxの利点

まずはじめに、ReactとReduxを使うメリットを再確認しておきたいと思います。 それぞれのメリットをしっかりと認識しておくことで、実装する際どう書くか迷ってしまった場合などにそのメリットを最大限活かす選択をすることができます。

Reactの利点

Reduxの利点

  • Reducer、ActionCreator、Componentといった利用側が書くべきモジュールはすべて純粋な関数で書ける
    • ほとんど関数の集合だけでアプリケーションが完成する
  • 副作用のある処理はすべて後述するMiddleware層に持っていける
  • 上記の理由からテストが非常に書きやすい

React/Reduxはこう書く!!

では実際に私がReact/Reduxアプリケーションを実装する際指標としているプラクティスを解説していきたいと思います。

Container Componentsの分割

Container Components とはRedux Storeconnect しているコンポーネントのことです。 詳細はReduxの公式Docをご確認ください。

Container Componentsを適切に分割することでコードの見通しを良くします。 Containerの中にContainerが存在する、 Container in Container も許容します。

分割されていない例

import { connect } from 'react-redux'
import * as React from 'react'
import ComponentA from '../components/ComponentA'
import ComponentB from '../components/ComponentB'
import * as actions from '../actions'

class BadContainer extends React.Component {
  componentDidMount() {
    this.props.fetch()
  }
  render() {
    return (
      <div>
         <ComponentA
          name={this.props.nameA}
          handler1={this.props.handlerA1}
          handler2={this.props.handlerA2}
        />
        <ComponentB
          name={this.props.nameB}
          handler1={this.props.handlerB1}
          handler2={this.props.handlerB2}
        />
     </div>
    )
  }
}

const mapStateToProps = state => {
  return {
    nameA: state.a.name,
    nameB: state.b.name
  }
}

const mapDispatchToProps = dispatch => {
  return {
    handlerA1() {
      dispatch(actions.actionA1())
    },
    handlerA2() {
      dispatch(actions.actionA2())
    },
    handlerB1() {
      dispatch(actions.actionA1())
    },
    handlerB2() {
      dispatch(actions.actionA2())
    },
    fetch() {
      dispatch(actions.fetchData())
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(BadContainer)

この例では一つのContainerで2つのコンポーネント、ComponentAとComponentBを表示しようとしています。 今回の例では2つだけですが、今後3つ、4つと表示するコンポーネントが増えていくとその分必要なハンドラーやプロパティが増えていくため、 mapStateToPropsmapDispatchToProps が肥大化してしまい見通しが悪くなります。

分割されている例

// GoodContainer.jsx
import { connect } from 'react-redux'
import * as React from 'react'
import ContainerA from './ContainerA'
import ContainerB from './ContainerB'
import * as actions from '../actions'

class GoodContainer extends React.Component {
  componentDidMount() {
    this.props.fetch()
  }
  render() {
    return (
      <div>
        <ContainerA />
        <ContainerB />
      </div>
    )
  }
}

const mapDispatchToProps = dispatch => {
  return {
    fetch() {
      dispatch(actions.fetchData())
    }
  }
}

export default connect(()=> { return {} }, mapDispatchToProps)(GoodContainer)

// ContainerA.jsx

import { connect } from 'react-redux'
import * as React from 'react'
import ComponentA from '../components/ComponentA'
import * as actions from '../actions'

const mapStateToProps = state => {
  return {
    name: state.a.name,
  }
}

const mapDispatchToProps = dispatch => {
  return {
    handler1() {
      dispatch(actions.actionA1())
    },
    handler2() {
      dispatch(actions.actionA2())
    },
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(ComponentA)

// ContainerB.jsx

import { connect } from 'react-redux'
import * as React from 'react'
import ComponentB from '../components/ComponentB'
import * as actions from '../actions'

const mapStateToProps = state => {
  return {
    name: state.b.name,
  }
}

const mapDispatchToProps = dispatch => {
  return {
    handler1() {
      dispatch(actions.actionB1())
    },
    handler2() {
      dispatch(actions.actionB2())
    },
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(ComponentB)

分割されている例では、もともと componentDidMount でデータフェッチしていた箇所だけを元のContainerに残し、 ComponentAComponentB に関するプロパティとハンドラーをそれぞれ ContainerAContainerB に分割しています。 こうすることでコードの肥大化を防ぎ見通しがかなり良くなったと思います。 ただし、全てのコンポーネントconnected にするとやりすぎなので適切な単位で分割する必要がありますのでご注意ください。 例えばECサイトの場合、商品名商品コメント配送方法、のようなおおざっぱな単位で分割していき、それでもコードの見通しが悪いと感じたら更に分割するというような感じで実装しています。

またContainer Componentsのテストの仕方については以前書いた記事がありますので読んで頂けると幸いです。 https://qiita.com/hiyamamoto/items/281709cc2a98268fb6c2

Presentational Componentsの分割

前項に続きコンポーネントの分割についてです。 Presentational Components についての詳細はReduxの公式Docをご確認ください。

Presentational Components を分割する理由としては前項と同様に見通しを良くするため、テストのしやすさのためです。 また、Reactのメリットにも書いてある再利用性を高めるという利点もあります。

まずは分割されていない例を見ていきましょう。

適切に分割されていない例

function Page(props) {
  return (
    <div>
      <div>
        <h1>{props.headerTitle}</h1>
      </div>>
      <div>
        <button>進む</button>
        <button>戻る</button>
        {props.value}
      </div>
      <div>
        <a href="/path/1">Link1</a>
        <a href="/path/2">Link2</a>
      </div>
    </div>
  )
}

こちらの例では複数の divbuttona 要素が出てきています。 そんなに大きなコンポーネントではありませんが既にちょっと見通しが悪くなっていると思います。

また、このコンポーネントのテストは以下になります。 ※ コンポーネントのテストは enzyme というライブラリの使用を前提としています

describe('<Page />', () => {
  const wrapper = shallow(<Page />)

  it('render 進むButton', () => {
    // button が複数出てくるのでうまく特定出来ない
    expect(wrapper.find('button').fisrt().contains('進む')).to.be.true
  })
})

このように複数の同一要素がある場合は wrapper.find('button').first() のように表示順を意識してコンポーネントの取得をする必要が出てきてしまい、ただ表示順が変わっただけでテストが壊れてしまいます。

では分割されている例を見てみましょう。

適切に分割されている例

function Page(props) {
  return (
    <Container>
      <Header title={props.headerTitle} />
      <Body>
        <ForwardButton onClick={props.onClickForward} />
        <BackwardButton onClick={props.onClickBackward} />
        <BodyContent value={props.value} />
      </Body>
      <Footer>
        <Link1 />
        <Link2 />
      </Footer>
    </Container>
  )
}

分割されている例では分割されていない例で見られた div などが一切出てきていないため見通しが良くなっているのがわかると思います。 また、それぞれのコンポーネントの意味がコンポーネント名からひと目で分かるようになっています。

このコンポーネントのテストはこちらです。

describe('<Page />', () => {
  const wrapper = shallow(<Page />)

  it('render 進むButton', () => {
    // ForwardButton というコンポーネントがあるかテストするだけで良い
    expect(wrapper.find(ForwardButton)).to.have.length(1)
  })
})

進むボタンコンポーネント化されたため、戻るボタンとの表示順を気にする必要がなくなり、デザインの都合で表示順が変わったとしてもテストが壊れることがなくなりました。

ComponentをStatelessに保つ

Reactの利点として状態をDOMから分離できるということを前述しましたが、コンポーネントからも状態を分離することで本来のコンポーネントが持つViewとしての役割だけに専念させることができます。 Reactの基本として state というもので状態を管理することができますが、その state そのものを持たないコンポーネントStateless Functional Component と呼びます。

React/Reduxアプリケーションでは state は基本的に redux store で管理し、コンポーネントでは state を使わないようにすることで状態管理を一元化することができます。

ただし、ボタンクリック時にモーダルを表示したり、フォーカスしたときに値を加工するだけなど、そのコンポーネント内で完結するような state を持つことはさほど問題にはならず、全ての状態を redux store で管理しなければいけないというわけではないと個人的には考えています。 あるコンポーネントの状態を別のツリーのコンポーネントで参照したい場合や、ドメインロジックに関わる状態は redux store で管理し、コンポーネント内部で完結する状態はそのコンポーネントstate として管理するように柔軟に設計するのがベターです。

Stateless Functional Component の書き方

statelifecycleメソッド を持たない場合は class シンタックスではなく、関数を使います。 コンポーネント名として関数名が使われるため、 arrow function より通常の関数として書くとよいです。

function FooComponent(props)  {
  return (
    <div>
      名前: {props.name}
      年齢: {props.age}
    </div>
  )
}

利点

  • ただの関数なのでテストしやすい
  • stateを持ってないことが一発でわかる

Reducerの分割

Reduxでは combineReducers という関数を使って通常Reducerを分割すると思います。 Reduxの公式DocではReducerを分割する際、コンポーネントレンダリングツリーで分割するのではなくドメインデータごとに分割することを推奨しています。 また、 DomainStateAppStateUIState という3つのStateに分割することが提案されています。

DomainState

例えば、商品や取引などのドメイン特有のstateのことです。 前述したレンダリングツリーごとの分割とドメインデータごとの分割の比較をしてみます。

レンダリングツリーごとの分割

レンダリングツリーごとの分割は簡単に言うと画面ごとの分割ということです。 下記は 新規画面編集画面一覧画面 といった画面ごとに分割している例です。

reducers
 ├── newReducer.js
 ├── editReducer.js
 └── listReducer.js

ドメインデータごとの分割

変わってドメインデータごとの分割では、商品配送方法ブランド などのドメインデータで分割しています。

reducers
 ├── productReducer.js
 ├── shipphingReducer.js
 └── brandReducer.js

AppState

アプリケーション全体の stateドメインデータ用のReducerとは別のReducerとして用意すると、見通しが良くなります。 例えば、データをローディング中かどうかを管理する isLoading などの state はこちらに含めます。

UIState

モーダルの表示状態などのUI特有の state も別のReducerで管理します。 ただし、前述のようにコンポーネントstate として持つことも多いです。

stateはPOJO(Plain Old JavaScript Object)

state は基本的に immutable(不変) object として扱います。 下記のようなコードはNGです。

const defaultState = {
  foo: 'foo',
  bar: 'bar'
}
const reducer(state = defaultState, action) => {
  if (action.type === 'Foo Action') {
    state.foo = action.payload  // fooだけ変更したい
    return state
  }
}

immutable.js を使えばかんたんに不変オブジェクトを生成できます。

import { Map } from 'immutable'
const defaultState = Map({
  foo: 'foo',
  bar: 'bar'
})
const reducer(state = defaultState, action) => {
  if (action.type === 'Foo Action') {
    return state.set('foo', action.payload)  // fooだけ変更したい
  }
}

上記の Map.prototype.set は常に新しい Mapインスタンスを返します。

ただし、 Map は普通のオブジェクトのようにプロパティにアクセスできないので、コンポーネント内でアクセスする際には state.get('foo') のようにする必要があります。 また、APIなどから取得してきたJSONを毎回 Map オブジェクトに変換する必要があるため結構面倒です。

それ、ES2015+でできるよ

const defaultState = {
  foo: 'foo',
  bar: 'bar'
}
const reducer(state = defaultState, action) => {
  if (action.type === 'Foo Action') {
    return {
      ...state,
      foo: action.payload // fooだけ変更できる!
    }
  }
}

state は色々な場所(コンポーネントAPIクライアントなど)でアクセスするので、「immutableオブジェクトになってるか?」といちいち判定するのは面倒です。 常に POJO にしておくことでその手間を減らすことができます。

Reducerからドメインロジックを分離したい場合

ドメインデータ用のReducerにドメインロジックを書いてしまうとコードがどんどん肥大化していってしまうため、下記のようにドメインデータを models ディレクトリ配下に書きたくなると思います。

import MyClass from '../models/MyClass'
const defaultState = new MyClass({
  foo: 'foo',
  bar: 'bar'
})
const reducer(state = defaultState, action) => {
  if (action.type === 'Foo Action') {
    // ドメインロジックはドメインクラスに委譲
    return state.changeFoo(action.payload)
  }
}

このようにしたい場合は models 配下からクラスではなく関数をエクスポートすることで代替できます。

import { changeFoo } from '../models/MyDomainModel'
const defaultState = new MyClass({
  foo: 'foo',
  bar: 'bar'
})
const reducer(state = defaultState, action) => {
  if (action.type === 'Foo Action') {
    // ドメインロジックはドメインモデルの関数に委譲
    return changeFoo(state, action.payload)
  }
}
export const changeFoo = (model, value) => {
  // 複雑な処理
  return {
    ...model,
    foo
  }
}

他にも immutable.jsのRecordを使う方法などがありますが、前述の通り変換の手間などを考えると個人的にはあまりおすすめしません。

FSA(Flux Standard Action)を使う

Flux Standard Action とは

https://github.com/redux-utilities/flux-standard-action

actionの型が実装者によってまちまちになると読みづらいし不便だから標準化しましょうね、という話です。 割と界隈ではデファクトになってる気がします。

非同期処理の成功、失敗ごとに LOAD_SUCCESSLOAD_FAILURE のようにactionを分けるのではなく LOAD_FINISH のように一つにまとめることが出来ます。

{
  type: 'LOAD_FINISH',
  payload: {
    id: 1,
    text: 'Do something.'  
  },
  meta: {
    ... // metadata
  }
}
{
  type: 'LOAD_FINISH',
  payload: new Error(),
  error: true
}

アクションの型が統一されて書く方も読む方も負荷が減るのでおすすめです。

非同期処理

reduxの登場人物(component, reducer, actionCreator)は純粋な関数の集まりなので副作用のある処理を書く場所がありません。 一般的に副作用のある処理は Middleware層 を利用します。

非同期処理をする為のMiddlewareとしては redux-thunkredux-saga が有名です。

redux-thunk vs redux-saga

redux-thunk

Pros

  • APIがかんたんで学習コストがほぼ0
  • サッと導入してサッと書ける

Cons

  • actionCreatorにロジックが入り込む
  • actionCreatorから純粋さがなくなる
  • そのため actionCreatorのテストが複雑になる

redux-saga

Pros

Cons

  • 学習コストが高い
    • Generator関数はとっつきにくい
    • APIが多い

小さめのアプリケーションでは redux-thunk、大きくて複雑なアプリケーションでは redux-saga、 というように使い分けるといいでしょう。

まとめ

どのフレームワークにも言えることですが、見通しがよくメンテしやすいアプリケーションを書くためにはコードを適切な単位で分割することが非常に重要です。 React/Reduxアプリケーションではコンポーネントを分割し再利用性を高めたり、状態を適切に分割することで、それぞれのメリットを最大限に活かせると思います。

Reduxの公式Docでは今回書いたReduxアプリケーションを設計する上での考え方が詳細に書かれていて非常に参考になるのでぜひご一読ください。

株式会社エニグモ 正社員の求人一覧

hrmos.co

SQLでバスケット分析(信頼度・リフト値算出)を実行

はじめに

こんにちは。2018年9月入社でデータ分析担当の@Tawasshyです。 この記事はEnigmo Advent Calendar 2018の3日目です。

弊社ではファッションECサイトであるBUYMAを展開しております。 売り手となるバイヤー(パーソナルショッパー)は世界中に在住しており、多種多様な商品を買い付けして膨大なSKUが存在します。 一方で、買い手側の購買行動も多岐に渡るのも必然となります。 そのような複雑な関係性を分析する状況においても、今回紹介するバスケット分析のような、基本的とも言える分析手法は解釈もしやすく、効果的な手段の一つになり得ると思っております。

バスケット分析について

バスケット分析は何と何の商品が一緒に買われているかに着目する分析方法であり、マーケット・バスケット分析、アソシエーション分析とも呼ばれています。

一般的な話

有名な事例としては、「おむつとビール」が同時に買われやすい、というルールの発見があります。 また、Agrawal氏らが1994年に発表した論文「Fast algorithms for mining association rules」(IBM Almaden Research Center)が、この分析手法が普及したきっかけとなったことが有力な説のようです。

なぜSQLでやるのか

バスケット分析はR言語のarulesというライブラリを使うのが、もっとも手軽な方法であると思いますが、今回はSQLでの実行を紹介します。

弊社ではエンジニアだけではなく、ディレクターやマーケター、カスタマサポートといったビジネスサイドのメンバーもSQLを日常的に叩いており、SQLでの分析の幅を拡げることに貢献できたら良いなと思ったことがSQLでやる理由です

バスケット分析詳細

まず、二つの商品X、Yがあったとします。 商品Xが買われた場合、商品Yという商品を買う確率を扱います。その指標として以下の指標を扱います。

今回算出する指標

  • 信頼度(confidence) 商品Xが買われた場合に、商品Yを買う確率。

  • リフト値 商品Yが買う確率が、商品Xを買った場合に商品Yを買う確率がどれだけ変化したかの倍率。 (信頼度➗商品Yが買われる確率) Xを買ってYを買う確率がどれだけ持ち上がったかと解釈できる。

今回算出しない値

  • 支持値(support) XとYが同時に買われている確率を示します。バスケット分析においてはこれも重要な指標ですが、今回は説明を割愛します。

環境構築について

手元のローカルマシンで簡単に再現できるのでSQLiteでやってみます。 macOSの場合は以下のようにbrewで一発です。なお、Windowsでも手軽にインストールできますが、ここでは割愛します。

$ brew install sqlite3

データの準備について

SQLiteを起動します。カレントディレクトリにbasket.sqlite3というの名のDBが生成され、DBに接続した状態となります。

$ sqlite3 basket.sqlite3

無事に起動できたらこのようにterminalにこのように表示されます。

sqlite>

まず、にテーブルの定義を行います。

create table sales_history(
  item_category char(12),
  user_id char(8)
);

購買履歴を作成します。

insert into sales_history values
  ('トップス','UID100001'),
  ('財布','UID100001'),
  ('財布','UID100002'),
  ('トップス','UID100002'),
  ('','UID100002'),
  ('トップス','UID100002'),
  ('アウター','UID100002'),
  ('アウター','UID100102'),
  ('ワンピース','UID100103'),
  ('','UID100104'),
  ('財布','UID100104'),
  ('','UID100105'),
  ('','UID100106'),
  ('トップス','UID100107'),
  ('トップス','UID100108'),
  ('','UID100109'),
  ('財布','UID100110'),
  ('','UID100111'),
  ('トップス','UID100111'),
  ('','UID100112'),
  ('','UID100112'),
  ('トップス','UID100113'),
  ('財布','UID100113'),
  ('ワンピース','UID100114'),
  ('ワンピース','UID100114')
;

これでデータの準備はできました。

バスケット分析の実行

信頼度の算出

select
    combi_count.item_category,
    combi_count.item_category2,
    cast(combi_count.order_count as real)/item_count.order_count as confidence
from(
  select
      item_category,
      count(distinct user_id) as order_count
  from
    sales_history
  group by
    item_category
  )item_count
inner join(
  select
      X.item_category,
      Y.item_category as item_category2,
      count(distinct X.user_id) as order_count
  from
    sales_history as X
  inner join
    sales_history Y
       on X.user_id = Y.user_id
       and X.item_category  Y.item_category
  group by
    X.item_category,Y.item_category
)combi_count on combi_count.item_category = item_count.item_category
;

以下のように出力されます。 商品X、商品Y、信頼度という順番です。

商品X 商品Y 信頼度
アウター トップス 0.5
アウター 財布 0.5
アウター 0.5
トップス アウター 0.166666666666667
トップス 財布 0.5
トップス 0.333333333333333
財布 アウター 0.2
財布 トップス 0.6
財布 0.4
アウター 0.142857142857143
トップス 0.285714285714286
財布 0.285714285714286

財布を買ったユーザーがトップスを買う信頼度がもっとも高いことが分かります。

リフト値も追加して算出

上のsqlが書ければ、全体でYが買われている確率を算出し、joinして信頼度を割ればリフト値が算出できます。

select
    combi_count.item_category,
    combi_count.item_category2,
    cast(combi_count.order_count as real)/item_count.order_count as confidence,
    cast(combi_count.order_count as real)/item_count.order_count / AllBuyY.order_count as lift
from(
  select
      item_category,
      count(distinct user_id) as order_count
  from
    sales_history
  group by
    item_category
  )item_count
inner join(
  select
      X.item_category,
      Y.item_category as item_category2,
      count(distinct X.user_id) as order_count
  from
    sales_history as X
  inner join
    sales_history Y
       on X.user_id = Y.user_id
       and X.item_category  Y.item_category
  group by
    X.item_category,Y.item_category
)combi_count on combi_count.item_category = item_count.item_category
-- 全体でYが買われている確率を算出してjoinする
inner join
(
  select
      item_category,
      cast(count(distinct user_id) as real) /
      (select count(distinct user_id) from sales_history) as order_count
  from
    sales_history
  group by
    item_category
) AllBuyY on combi_count.item_category2 = AllBuyY.item_category
;

以下のように、出力されます。 商品X、商品Y、信頼度、リフト値を出しております。

商品X 商品Y 信頼度 リフト値
アウター トップス 0.5 1.25
アウター 財布 0.5 1.5
アウター 0.5 1.07142857142857
トップス アウター 0.166666666666667 1.25
トップス 財布 0.5 1.5
トップス 0.333333333333333 0.714285714285714
財布 アウター 0.2 1.5
財布 トップス 0.6 1.5
財布 0.4 0.857142857142857
アウター 0.142857142857143 1.07142857142857
トップス 0.285714285714286 0.714285714285714
財布 0.285714285714286 0.857142857142857

信頼度、リフト値について

ある商品を買う確率としての指標として、どちらが大事というのはケースバイケースとなりますが、 両方が高い値でないとユーザーの傾向を示す効果的な分析にならないことが言えます。

リフト値が高く、信頼度が低い場合

全体で商品X, あるいは商品Y自体を買われている確率が全体で低い場合に起きます。 商品Xが買われることが少ない為、リフト値が高い値を示してもYを買う確率は小さいままになります。 また、商品Y自体を買われている確率が全体で低い場合は、リフト値が掛けかれる元の確率が低いことになる為、Yを買われる確率は低いままということになります。

リフト値が低く、信頼度が高い場合

何もしなくてもYを買われる確率が高く、Xを買っても買わなくても、Yを買う確率の増減が小さい状態を示します。

そもそも購買履歴が少ない場合…

偶然、信頼度、リフトが高く出るということが起こり得るので、Xを買ったユーザーYの購買促進を行うキャンペーンなどの施策を行っても有効に働かない場合があります。

実際にDBに接続して行う場合は

同じテーブルへの問い合わせが散在しているので、PostgreSQL, MySQL, SQLServer等ではwith句は必須であると思います。

SQLでやるのがめんどくさいなと思ったら..

データを抽出する必要がありますが、R言語でarulesというライブラリを使えばもっと手軽にできますし、無料です。

さいごに

弊社ではデータ分析に取り組む一方、データ分析基盤を整え、データ活用の効率化とアクセスビリティを推進しております。 エンジニアはもちろん、データを活用しながら「世界を買える」ビジネスに挑戦してみたいメンバーのジョインをお待ちしております!

やんちゃであれ! ENIGMO7より

参考

https://www.albert2005.co.jp/knowledge/marketing/customer_product_analysis/abc_association

https://codezine.jp/article/detail/10284

AMP仕様の要点まとめ

こんにちは、Enigmo 新卒エンジニアの@sean0628_iです。 Enigmo Advent Calendar 2018 2日目の記事です。

初めに

Accelerated Mobile Pages の略であり、高速でスムーズにWeb ページを表示するためのライブラリー、或いはその仕組みのことです。 通常のHTML に比べて、制限が多く存在します。 今回はAMP 公式のドキュメントの仕様に関する箇所が英語だったので、読解し要点をまとめます。

AMP 制限

  • 特定の要素をマークアップに含める。
  • JavaScript は利用不可。
  • inline CSS のみ利用可。 -> 50KB の制限付き
  • 特定のHTML tag は利用不可。
  • HTML 内、コメント不可。
  • 特定のCSS properties は利用不可。

必要なマークアップ

  • <!doctype html> でHTML を書き始める。
  • <html ⚡> tag を含める。(<html amp> でも代用可能)
  • <head> tag と<body> tag を含める。(通常のHTML では任意)
  • <head> tag 内の一番初めに、<meta charset="utf-8"> を含める。
  • <head> tag 内に、<link rel="canonical" href="$SOME_URL"> を含める。($SOME_URL は通常版のHTML のURL、通常版のHTML が存在しない場合はAMP のURLを入れる。)
  • <head> tag 内に、<meta name="viewport" content="width=device-width"> を含める。minimum-scale=1 および、initial-scale=1 も含めることが勧められる。
  • <head> tag 内に、<script async src="https://cdn.ampproject.org/v0.js"></script> を含める。
  • <head> tag 内に、AMP Boilerplate Code(head > style[amp-boilerplate] と noscript > style[amp-boilerplate])を含める。

HTML tags

tag 適正
script ×(type がapplication/ld+json の場合のみ○)
noscript
base ×
img amp-img にて代用
video amp-video にて代用
audio amp-audio にて代用
iframe amp-iframe にて代用
frame ×
frameset ×
object ×
param ×
applet ×
embed ×
form ○(amp-form extension を含めることで利用可)
input elements 一部(<input[type=image]>、<input[type=button]>、<input[type=password]>、<input[type=file]>)を除いて○<fieldset>、<label> は○
button
style <head> tag 内に、一つのみ追加可。amp-custom attribute を持たせる必要あり。
link microformats.org に、登録されているrel は○
meta http-equiv attribute が特定の値に利用可。詳細: AMP validator specification
a href attribute をjavascript: から始めてはいけない。target を設定する場合、_blank を利用。
svg 殆どのSVG は利用可

Comments

  • HTML 内でコメントは利用できない。

CSS

  • 50,000 bytes の上限を超えてはいけない。
  • !important は利用できない。
  • transition および、animation 関連はopacitytransform properties のみ利用可。

新卒Rubyエンジニアがオススメする実務で役にたった技術書5選

こんにちは、18年新卒エンジニアの@sean0628_iです。 早いもので今年ももう12月、Enigmoにジョインしたのが今年の春なので、気がついたら入社してから半年ほどが経っていますね。。。

さて、12月といえばAdvent Calendar の季節ですねー笑

今年はEnigmoも初めてAdvent Calendar を公開することとなりました。 というのも、新卒の私が「面白そうだなぁー。」、とボソッと呟いたところ、部長殿から「やってみたらー。」、とOKをいただきましたので、 他のメンバーにもご協力をいただき、Enigmo Advent Calendar 2018 を公開することになりました。

記念すべきEnigmo Advent Calendar 2018の1日目は、文系学部から新卒でエンジニアになった私が今までに読んで、実際に業務で役に立った技術書5選をお届けします。

そもそもAdvent Calendarとは?

さっきから、「”Advent Calendar”とか、横文字多用しやがって。」、という方、大変失礼しました、、、ご説明します。

Advent Calendar とは、元々クリスマスまでの日数を数えるためのカレンダーで、日ごとに小さなポケットを設け、そこにお菓子を入れて楽しんだりするものだそうです。

数年前からWeb 業界の企業ではではそれに倣って、クリスマスまでの25日間記事を公開するようになりました。 つまり、我々が意味するAdvent Calendar とは、25日間記事を公開し続けるという言わば苦行のことです。 ただ、エンジニアとして新たな知識の習得、既知の知識の共有は避けられません。 ということで、今回は苦行を断行します。

* なお、苦行ゆえ時々お休みすることもあるかと思いますが、その点はご容赦いただければと思います。

オススメの技術書5選

1. プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで (Software Design plusシリーズ)

こちらの本は、知人の勧めで入社する前に読みました。当時は本当にRuby初心者、というかプログラミング言語初心者でした。 そんな私でも途中で挫折せず、Rubyを学べたのはこの本のおかげかなぁと。

この本では実際に手を動かして学習することが出来ます。 ひたすら読むだけだと飽きてしまいますし、言語習得のためには実際に手を動かすのが一番かとも思います。 そういった意味でこの本はオススメです。

2. Ruby on Rails 5アプリケーションプログラミング

上記の本でRubyを一通り学んだ後、こちらでRails を学びました。

もし、アプリ作成の一連の流れを知りたいということであれば、Rails tutorial がオススメです。

一方で、 この本では、Rails 組み込みのメソッドや、Rails の仕組みが事細かに紹介されています。 Rails を体系的に学びたいという方や、Rails tutorial やったけどもう少し深くRails を知りたいという方にはこの本がオススメです 。

3. Everyday Rails - RSpecによるRailsテスト入門

この本は入社直後、研修の一環で読みました。

当時は、午前中に本を読み、午後に実務でアウトプットするという形式でした。右も左もわからず、テストを書くのが本当に嫌いでした笑 しかし、この本で基礎を学び、実務でのテストコードの実装を重ねた結果、今ではテストを書くことが苦ではなくなりました。 さらには、テストコードがないソースコードを見ると不安を覚える身体になってしまいました笑 とてもわかりやすいのでオススメです。

4. スッキリわかる SQL 入門 ドリル215問付き! (スッキリシリーズ)

実務では主にRuby on Rails を使用しているのですが、事あるごとにSQLの記述に迫られます。

ちなみに、Enigmoのメンバーは、エンジニアに限らず殆どの人がSQLを扱う事ができます。 もともとマーケティング業界でインターンシップをしていた私には、これがとても衝撃でした。

ともかくも、この本が良かった点はブラウザ上に環境が整備されているので事前の準備が不要で、すぐにSQLを学習できたということです。 実践問題も付いているので、この本一冊でSQLの基本をしっかりと身に付けることができると思います。

5. リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

プログラミングを進める上で必要な命名規則、コード記述の作法に関して学びました。

正直に話すと、プログラミングを初めて1ヶ月くらいの時にこの本を読みました。 その時は、大した気づきもなく「レビュー高い割に、ふーん。」、くらいな感想しかありませんでした。 しかし、つい最近読み返してみると、当たり前だけど現状できていないことが多く記述されていることに気がつきました。

Enigmoのエンジニアの卓上でもちらほら見かけますし、一読の価値はあると思います。

所感

駆け足になりましたが、新卒の私がオススメする実務で役立った技術書5冊上げてみました。

これまでに読んだ本で他にも紹介したい良書はたくさんあります。オブジェクト指向の話とか、DB設計の話とかたくさん。 しかし、今回は日々Rubyを扱うエンジニアとして、言語系で実務に役立った本に的を絞り紹介してみました。 また機会があれば、他の良書も紹介したいと思います。この辺で、Enigmo Advent Calendar 2018 1日目終わりたいと思います。

最後まで読んでいただきありがとうございます。