やっちのわいわい日記

電通大に編入した元高専生の日記。日記を書きつつ編入のこととか勉強のこととか書こうと思っています。編入とかで質問があるかたは@amhflcl0514にDMくださいお

Elixir勉強日記6 〜モジュール編〜

Elixir勉強日記6回目です

前回で関数まわりに触って若干お腹いっぱいになってサボり過ぎました

続きを進めます

f:id:nishikino3:20190611145254j:plain

elixirschool.com

モジュール... defmodule的な?

モジュール

私たちは経験的に、全ての関数を1つの同じファイルとスコープに持つと手に負えないことを知っています。 このレッスンでは関数をまとめ、構造体として知られる特別なマップを定義することで、コードをより効率のよい形に組織化する方法を取り上げます。

スコープを切り分けるためにあるんですね(情弱)

モジュール

モジュールは関数群を名前空間へと組織する最良の方法です。 関数をまとめることに加えて、関数のレッスンで取り上げた名前付き関数やプライベート関数を定義できます。

落ち着いて考えたらクラスないしないと困りますね

基本的な例を見てみましょう:

defmodule Example do
  def greeting(name) do
    "Hello #{name}."
  end
end

iex> Example.greeting "Sean"
"Hello Sean."

Elixirではモジュールをネストすることが可能で、機能ごとにさらなる名前空間をつけることができます:

defmodule Example.Greetings do
  def morning(name) do
    "Good morning #{name}."
  end

  def evening(name) do
    "Good night #{name}."
  end
end

iex> Example.Greetings.morning "Sean"
"Good morning Sean."

.でネストの表現するんですね〜(Rubyだと:

モジュールの属性(attribute)

モジュール属性はElixirでは一般に定数として用いられることがほとんどです。 単純な例を見てみましょう:

defmodule Example do
  @greeting "Hello"

  def greeting(name) do
    ~s(#{@greeting} #{name}.)
  end
end

どうゆうことだってばよ...

重要なので言及しておきますと、 Elixir には予約されている属性があります。 もっとも一般的なのは以下の3つです:

  • moduledoc — 現在のモジュールにドキュメントを付けます。
  • doc — 関数やマクロについてのドキュメント管理。
  • behaviour — OTP またはユーザが定義した振る舞い(ビヘイビア)に用います。

あーー属性ってのはモジュール内で共通して扱う値のことなんですね

日本語にするとなんか微妙にぱっとしないですね

調べたら属性 => attributesでした

日本語分かりくすぎる

構造体(Structs)

構造体は定義済みのキーの一群とデフォルト値を持つ特殊なマップです。 モジュール内部で定義されなくてはならず、そのモジュールから名前をとります。 構造体にとっては、モジュール内部で自身しか定義されていないというのもありふれたことです。

構造体ってきくとCを思い出しますが...

モジュール内で定義されるマップの属性みたいな感じなんすね

構造体を定義するには defstruct を用い、フィールドとデフォルト値のキーワードリストを添えます:

defmodule Example.User do
  defstruct name: "Sean", roles: []
end

いくつか構造体を作ってみましょう:

iex> %Example.User{}
#Example.User<name: "Sean", roles: [], ...>

iex> %Example.User{name: "Steve"}
#Example.User<name: "Steve", roles: [], ...>

iex> %Example.User{name: "Steve", roles: [:manager]}
#Example.User<name: "Steve", roles: [:manager]>
# 構造体はあたかもマップのように更新することができます:

iex> steve = %Example.User{name: "Steve"}
#Example.User<name: "Steve", roles: [...], ...>
iex> sean = %{steve | name: "Sean"}
#Example.User<name: "Sean", roles: [...], ...>

構造体はマップのように更新できるのね...

パターンマッチングが未だに慣れない...

最も重要なことですが、構造体はマップに対してマッチすることができます:

iex> %{name: "Sean"} = sean
#Example.User<name: "Sean", roles: [...], ...>

Elixir 1.8以降、構造体にカスタムイントロスペクション機能が追加されました。

かすたむいんとろすぺくしょん...

カスタムイントロスペクションがどのように使われるのかを理解するため、 sean の中身を見てみましょう。

iex> inspect(sean)
"#Example.User<name: \"Sean\", roles: [...], ...>"

この例では全てのフィールドが出力対象になっていますが、出力したくない項目がある場合、どのようにしたら良いでしょうか? この場合、 @derive を利用することで実現することができます! roles を出力から除外したい場合、以下のように記述します。

defmodule Example.User do
  @derive {Inspect, only: [:name]}
  defstruct name: nil, roles: []
end
# 注記: @derive {Inspect, except: [:roles]} でも実現することができます。

モジュールを更新したら、 iex で確認してみましょう。

iex> sean = %Example.User{name: "Sean"}
#Example.User<name: "Sean", ...>
iex> inspect(sean)
"#Example.User<name: \"Sean\", ...>"
# roles が出力から除外されました!

モジュールの構造体から値を絞って出力するってことなんすかね

クラスのインスタンス変数のアクセス権的な扱いみたいになってるのでしょうか...?

いやモジュールを直で叩いたら構造体の全部の値出てくるから違うか(普段からinspectでモジュールを呼べってことなのか)

コンポジション(Composition)

さて、モジュールと構造体の作り方がわかったので、コンポジションを用いてモジュールや構造体に既存の機能を追加する方法を学びましょう。 Elixir は他のモジュールと連携する様々な方法を用意しています。

他のモジュールと連携

alias

モジュール名をエイリアスすることができます。 Elixir のコードでは頻繁に使われます:

defmodule Sayings.Greetings do
  def basic(name), do: "Hi, #{name}"
end

defmodule Example do
  alias Sayings.Greetings

  def greeting(name), do: Greetings.basic(name)
end

これを使うことでモジュール間でメソッドのやり取り(継承に近い?)感じなんですね

alias を使わない場合

defmodule Example do
  def greeting(name), do: Sayings.Greetings.basic(name)
end

2つのエイリアス間で衝突があったり、全体を別名でエイリアスしたい場合には、 :as オプションを使います:

defmodule Example do
  alias Sayings.Greetings, as: Hi

  def print_message(name), do: Hi.basic(name)
end

扱いやすい名前で扱えるのはありがたいですね

複数のモジュールを一度にエイリアスすることも可能です:

defmodule Example do
  alias Sayings.{Greetings, Farewells}
end

便利!

import

モジュールをエイリアスするよりも、関数を取り込みたいという場合には、 import を使います:

iex> last([1, 2, 3])
** (CompileError) iex:9: undefined function last/1
iex> import List
nil
iex> last([1, 2, 3])
3

あっ普通はimportつかうんだ..

フィルタリング

デフォルトでは全ての関数とマクロが取り込まれますが、 :only や :except オプションを使うことでフィルタすることができます。

まあ全部使うわけじゃないですしね

pythonとかでよく見かける気がします

特定の関数やマクロを取り込むには、名前/アリティのペアを :only や :except に渡す必要があります。 last/1 で最後の関数のみを取り込んでみましょう:

iex> import List, only: [last: 1]
iex> first([1, 2, 3])
** (CompileError) iex:13: undefined function first/1
iex> last([1, 2, 3])
3

last/1 で指定された関数以外を全て取り込むには、同じ関数で試してみましょう:

iex> import List, except: [last: 1]
nil
iex> first([1, 2, 3])
1
iex> last([1, 2, 3])
** (CompileError) iex:3: undefined function last/1

名前とアリティのペアを渡すのってアリティ指定するのちょっとだるいですね

名前/アリティのペアに加えて、 :functions と :macros という2つの特別なアトムもあります。これらはそれぞれ関数とマクロのみを取り込みます:

import List, only: :functions
import List, only: :macros

どうでもいいけど:macrosってマクロスっぽい

require

他のモジュールのマクロを使用することをElixirに伝えるために require を使うことができます。 import とのわずかな違いは、関数ではなくマクロを使用可能とすることです。

defmodule Example do
  require SuperMacros

  SuperMacros.do_stuff
end

マクロを呼び出すのが require

まだロードされていないマクロを呼びだそうとすると、Elixir はエラーを発生させます。

それはそう

use

use マクロを用いることで他のモジュールを利用して現在のモジュールの定義を変更することができます。 コード上で use を呼び出すと、実際には提供されたモジュールに定義されている using/1 コールバックを呼び出します。 using/1 マクロの結果はモジュールの定義の一部になります。 この動作に対する理解を深めるために簡単な例を見ましょう:

ほかのモジュールを利用して現在のモジュールの定義を変更する...?

defmodule Hello do
  defmacro __using__(_opts) do
    quote do
      def hello(name), do: "Hi, #{name}"
    end
  end
end

マクロについても多少調べたほうがええんかな

とりあえずuseで呼び出すと__using__が呼び出されるって感じなんですね

__using__って名前Pythonに似てる気がしますね

ここでは hello/1 関数を定義する using/1 コールバックを定義した Hello モジュールを作りました。 この新しいコードを試すために新しいモジュールを作ります:

defmodule Example do
  use Hello
end

IExでこのコードを試して見ると Example モジュールで hello/1 を使えるのがわかります。

iex> Example.hello("Sean")
"Hi, Sean"

使えるようになる...???

defmacro __using__ が走ってhelloメソッドが生えてるってことかな

ここで use が Hello の using/1 コールバックを呼び出して、結果のコードをモジュールに追加します。 基本的な例を見せたので、ここからはこのコードを変更して using/1 にオプションをサポートする方法を見てみましょう。 greeting オプションを追加します:

defmodule Hello do
  defmacro __using__(opts) do
    greeting = Keyword.get(opts, :greeting, "Hi")

    quote do
      def hello(name), do: unquote(greeting) <> ", " <> name
    end
  end
end

新しく作った greeting オプションを含むために Example モジュールを更新します:

defmodule Example do
  use Hello, greeting: "Hola"
end

IExで試して見ると挨拶が変わるのを確認できます。

iex> Example.hello("Sean")
"Hola, Sean"

これらは use がどうやって動作するのかを説明する簡単な例でしたが、これは Elixir のツールボックスで信じられないほどに強力なツールです。 Elixir を学び続けたら use をあっちこっちで見ることになるでしょう。かならず見ることになりそうな例をひとつあげれば、 use ExUnit.Case, async: true です。

うーん実際にどう使うと便利なのかイメージがなかなかつかないので、使われているところを見たら考えよう(思考停止)

注意: quote 、 alias 、 use 、 require はメタプログラミングで使用してたマクロです。

まとめ

  • defmoduleでモジュール化できる
  • モジュールで扱うattributesをもつことができる
  • defstructを使うことでモジュールの値を構造体(Structs)としてマップのように扱うことができる
  • モジュール間を色々つなぎこむことができる
  • aliasで別のモジュールのメソッドをコピー?というか扱えるようになる
  • 普通はaliasではなくimportでメソッドとマクロをごっそり取り込む
  • requireでモジュールのマクロを使用可能にする
  • useでモジュールでdefmacro __using__で定義されたマクロが走る

Elixir勉強日記5 〜関数・パイプライン演算子編〜

Elixir勉強日記5回目です

最近はずっとRailsばっかりやっててElixir触るの忘れがちです

引き続き進めて生きます

f:id:nishikino3:20190611145254j:plain

elixirschool.com

今回はついに関数型言語において関数という題で進めることになります...!

5回目まで関数が出てこない関数型言語のドキュメントってある意味素晴らしいですよね

関数

Elixirや多くの関数型言語では、関数は第一級市民(≒ファーストクラスオブジェクト)です。 Elixirにおける関数の種類について、それぞれどう異なっていて、どのように使うのかを学んでいきます

いや関数型言語だから間違ってないけど草

匿名関数

その名前が暗に示している通り、匿名関数は名前を持ちません。 Enumのレッスンで見たように、匿名関数はたびたび他の関数に渡されます。 Elixirで匿名関数を定義するには、fnとendのキーワードが必要です。 これらの内側で、任意の数の引数と->で隔てられた関数の本体とを定義することができます。

iex> sum = fn (a, b) -> a + b end
iex> sum.(2, 3)
5

この辺の記法ってjsやってると違和感ないんでしょうかね

.の存在絶対忘れそう

&省略記法

匿名関数を利用するのはElixirでは日常茶飯事なので、そのための省略記法があります:

iex> sum = &(&1 + &2)
iex> sum.(2, 3)
5

おそらく見当が付いているでしょうが、省略記法では引数を&1、&2、&3などとして扱うことができます。

省略記法いいですけど知らない人見たら結構謎ですね

パターンマッチング

Elixirではパターンマッチングは変数だけに限定されているわけではなく、次の項にあるように、関数へと適用することができます。

Elixirはパターンマッチングを用いてマッチする可能性のある全てのオプションをチェックし、最初にマッチするオプションを選択して実行します:

iex> handle_result = fn
...>   {:ok, result} -> IO.puts "Handling result..."
...>   {:ok, _} -> IO.puts "This would be never run as previous will be matched beforehand."
...>   {:error} -> IO.puts "An error has occurred!"
...> end

iex> some_result = 1
iex> handle_result.({:ok, some_result})
Handling result...

iex> handle_result.({:error})
An error has occurred!

すごい!この分岐なかなか美しいですね!

_が意味するのはこの変数を使わないってことっぽいですかね

2つ目の関数には入らないみたいです

名前付き関数

関数を名前付きで定義して後から呼び出せるようにすることができます。 こうした名前付き関数はモジュール内部でdefキーワードを用いて定義されます。 モジュールについては次のレッスンで学習しますので、今のところ名前付き単体に着目しておきます。

モジュール内部で定義される関数は他のモジュールからも使用することができます。 これはElixirでは特に有用な組み立て部品になります

個人的には関数は名前をつけるものっていう感覚なので別に名前をつけるのが当然っていう感覚はないみたいですね

defmodule Greeter do
  def hello(name) do
    "Hello, " <> name
  end
end

iex> Greeter.hello("Sean")
"Hello, Sean"

関数本体が1行で済むなら、do:を使ってより短くすることができます:

defmodule Greeter do
  def hello(name), do: "Hello, " <> name
end

1行で書けてるけど実際こういう記述ってするんですかね 可読性びみょい気が

パターンマッチングの知識を身にまとったので、名前付き関数を使った再帰を探検しましょう:

defmodule Length do
  def of([]), do: 0
  def of([_ | tail]), do: 1 + of(tail)
end

iex> Length.of []
0
iex> Length.of [1, 2, 3]
3

身にまとったってなんやねんwww

慣れないと気持ち悪い例ですが理解できると美しいですね

これが関数型言語の魅力なんでしょうかね

関数の命名とアリティ

以前言及したとおり、関数は名前とアリティ(引数の数)の組み合わせで命名されます。 つまり、以下のようなことができるということです:

defmodule Greeter2 do
  def hello(), do: "Hello, anonymous person!"   # hello/0
  def hello(name), do: "Hello, " <> name        # hello/1
  def hello(name1, name2), do: "Hello, #{name1} and #{name2}"
                                                # hello/2
end

iex> Greeter2.hello()
"Hello, anonymous person!"
iex> Greeter2.hello("Fred")
"Hello, Fred"
iex> Greeter2.hello("Fred", "Jane")
"Hello, Fred and Jane"

関数名の一覧を上記のコメントに載せました。 例えば、1つめの実装は引数を取らないのでhello/0、2つ目は1つの引数を取るのでhello/1となります。 他の言語におけるオーバーロードとは違い、これらは互いに 異なる 関数として扱われます。 (さっき扱ったパターンマッチングは 同じ 数の引数を取る関数定義が複数ある場合のみ適用されます)

引数の数で異なる関数として認識されるんすね

この辺は他の言語と違うのでしっかり覚えておいた方がよさそうですね

関数とパターンマッチング

※この節ながい

内部では、関数は実行された時の引数をパターンマッチングしています。

マップを受け取るが、特定のキーにだけ関心がある関数が必要であるとしましょう。 私たちは次のようにキーの有無に基づいて引数をパターンマッチすることができます:

defmodule Greeter1 do
  def hello(%{name: person_name}) do
    IO.puts "Hello, " <> person_name
  end
end

関数の受け渡しにパターンマッチング使ってるので色々応用が効きそうですね

この辺りも頭に入れておかないと違和感しかないですね

今度はFredという名前の人物を表すマップを持っているとしましょう。

iex> fred = %{
...> name: "Fred",
...> age: "95",
...> favorite_color: "Taupe"
...> }

Greeter1.hello/1 をfredのマップで実行するとこのような結果となります:

# call with entire map
...> Greeter1.hello(fred)
"Hello, Fred"
# :nameキーを 含まない マップで関数を実行するとどうなるでしょうか?

# call without the key we need returns an error
...> Greeter1.hello(%{age: "95", favorite_color: "Taupe"})
** (FunctionClauseError) no function clause matching in Greeter1.hello/1

    The following arguments were given to Greeter1.hello/1:

        # 1
        %{age: "95", favorite_color: "Taupe"}

    iex:12: Greeter1.hello/1

このような挙動となる理由は、Elixirは関数が実行された際の引数を関数で定義されたアリティに対してパターンマッチさせているためです。

Greeter1.hello/1にデータが届いた際どのように見えるか考えてみましょう:

はぇ〜〜〜〜〜〜〜

# incoming map
iex> fred = %{
...> name: "Fred",
...> age: "95",
...> favorite_color: "Taupe"
...> }

Greeter1.hello/1は次のような引数を期待します:

%{name: person_name}

Greeter1.hello/1では、私たちが渡したマップ(fred)は引数(%{name: person_name})に対して評価されます:

%{name: person_name} = %{name: "Fred", age: "95", favorite_color: "Taupe"}

これは渡されたマップの中にnameに対応するキーを見つけます。 マッチがありました!このマッチの成功によって、右辺のマップ(つまり fredマップ)の中にある:nameキーの値は左辺の変数(person_name)に格納されます。

さて、Fredの名前をperson_nameにアサインしたいが、人物マップ全体の値も保持したいという場合はどうするのでしょう?挨拶を出力した後IO.inspect(fred)を使いたいとしましょう。 この時点では、マップの:nameキーだけをパターンマッチしているので、そのキーの値だけが変数に格納され、関数はFredの残りの値に関する知識を持っていません。

これを保持するためには、マップ全体を変数にアサインして使用できるようにする必要があります。

新しい関数を作ってみましょう:

defmodule Greeter2 do
  def hello(%{name: person_name} = person) do
    IO.puts "Hello, " <> person_name
    IO.inspect person
  end
end

Elixirは引数を渡されたままパターンマッチするということを覚えておいてください。 そのためこのケースでは、それぞれが渡された引数に対してパターンマッチして、マッチした全てのものを変数に格納します。 まずは右辺を見てみましょう:

person = %{name: "Fred", age: "95", favorite_color: "Taupe"}

ここでは、personが評価され、fredマップ全体が格納されました。 次のパターンマッチに進みます:

%{name: person_name} = %{name: "Fred", age: "95", favorite_color: "Taupe"}

これは、マップをパターンマッチしてFredの名前だけを保持したオリジナルのGreeter1関数と同じです。 これによって1つではなく2つの変数を使用することができます:

引数を2つのアリティでそれぞれパターンマッチングしてるのか...

personは%{name: "Fred", age: "95", favorite_color: "Taupe"}を参照します person_nameは"Fred"を参照します これでGreeter2.hello/1を実行したとき、Fredの全ての情報を使用することができます:

この例って関数渡す側はタプルで渡すけど関数側ではタプルの一部の情報を変数でもちたいっていうのが前提にあるって感じなんですかね

# call with entire person
...> Greeter2.hello(fred)
"Hello, Fred"
%{age: "95", favorite_color: "Taupe", name: "Fred"}
# call with only the name key
...> Greeter4.hello(%{name: "Fred"})
"Hello, Fred"
%{name: "Fred"}
# call without the name key
...> Greeter4.hello(%{age: "95", favorite_color: "Taupe"})
** (FunctionClauseError) no function clause matching in Greeter2.hello/1

    The following arguments were given to Greeter2.hello/1:

        # 1
        %{age: "95", favorite_color: "Taupe"}

    iex:15: Greeter2.hello/1

入ってきたデータに対して独立してパターンマッチして、関数の中でそれらを使用できるようにしたことで、Elixirは複数の奥行きでパターンマッチするという点を確認しました。

リストの中で%{name: person_name}とpersonの順序を入れ替えたとしても、それぞれがfredとマッチングするので同じ結果となります。

つまり関数渡す側からは順番を気にする必要がないと

変数とマップを入れ替えてみましょう:

defmodule Greeter3 do
  def hello(person = %{name: person_name}) do
    IO.puts "Hello, " <> person_name
    IO.inspect person
  end
end

Greeter2.hello/1で使用した同じデータで実行してみます:

# call with same old Fred
...> Greeter3.hello(fred)
"Hello, Fred"
%{age: "95", favorite_color: "Taupe", name: "Fred"}

%{name: person_name} = person}%{name: person_name}がpersonに対してパターンマッチしているように見えたとしても、実際には それぞれが 渡された引数をパターンマッチしているということを覚えておいてください。

覚えておきます

まとめてくれてありがとうございます↓

まとめ: 関数は渡されたデータをそれぞれの引数で独立してパターンマッチします。 関数の中で別々の変数に格納するためにこれを利用できます。

プライベート関数

他のモジュールから特定の関数へアクセスさせたくない時には関数をプライベートにすることができます。 プライベート関数はそのモジュール自身の内部からのみ呼び出すことが出来ます。 Elixirではdefpを用いて定義することができます:

defmodule Greeter do
  def hello(name), do: phrase() <> name
  defp phrase, do: "Hello, "
end

iex> Greeter.hello("Sean")
"Hello, Sean"

iex> Greeter.phrase
** (UndefinedFunctionError) function Greeter.phrase/0 is undefined or private
    Greeter.phrase()

defpですね!!おぼえましたし

ガード

制御構造レッスンでもガードについて少しだけ触れましたが、これを名前付き関数に適用する方法を見ていきます。 Elixirはある関数にマッチするとそのガードを全てテストします。

以下の例では同じ名前を持つ2つの関数があります。ガードを頼りにして、引数の型に基づいてどちらを使うべきか決定します:

defmodule Greeter do
  def hello(names) when is_list(names) do
    names
    |> Enum.join(", ")
    |> hello
  end

  def hello(name) when is_binary(name) do
    phrase() <> name
  end

  defp phrase, do: "Hello, "
end

iex> Greeter.hello ["Sean", "Steve"]
"Hello, Sean, Steve"

これだと関数はオーバーロードされない感じなんですかね

型が指定されているから別の関数として扱われてるってことなの...?

ちょっと調べたらガード節が書いてあると同名関数でも別の処理をされるっぽいですね

デフォルト引数

引数にデフォルト値が欲しい場合、引数 \ デフォルト値の記法を用います

バックスラッシュふたつ

イコール置きたいところですがそれやるとさっきのパターンマッチングの例の通り動作して正常に動かないんですかね?

間違えそう

defmodule Greeter do
  def hello(name, language_code \\ "en") do
    phrase(language_code) <> name
  end

  defp phrase("en"), do: "Hello, "
  defp phrase("es"), do: "Hola, "
end

iex> Greeter.hello("Sean", "en")
"Hello, Sean"

iex> Greeter.hello("Sean")
"Hello, Sean"

iex> Greeter.hello("Sean", "es")
"Hola, Sean"

この例もしれっとパターンマッチングして呼び出す関数を分けてますね〜(phrase/1)

慣れないと結構気持ち悪い

先ほどのガードの例をデフォルト引数と組み合わせると、問題にぶつかります。 どんな風になるか見てみましょう:

defmodule Greeter do
  def hello(names, language_code \\ "en") when is_list(names) do
    names
    |> Enum.join(", ")
    |> hello(language_code)
  end

  def hello(name, language_code \\ "en") when is_binary(name) do
    phrase(language_code) <> name
  end

  defp phrase("en"), do: "Hello, "
  defp phrase("es"), do: "Hola, "
end

** (CompileError) iex:31: definitions with multiple clauses and default values require a header.
Instead of:

    def foo(:first_clause, b \\ :default) do ... end
    def foo(:second_clause, b) do ... end

one should write:

    def foo(a, b \\ :default)
    def foo(:first_clause, b) do ... end
    def foo(:second_clause, b) do ... end

def hello/2 has multiple clauses and defines defaults in one or more clauses
    iex:31: (module)

Elixirは複数のマッチング関数にデフォルト引数があるのを好みません。混乱の元になる可能性があります。 これに対処するには、デフォルト引数付きの関数を先頭に追加します:

好まないからコンパイルエラーだぜ!!!

同名関数にそれぞれ同一のデフォルト引数があるのは気持ち悪いからやめてクレメンスってことなんすね

defmodule Greeter do
  def hello(names, language_code \\ "en")

  def hello(names, language_code) when is_list(names) do
    names
    |> Enum.join(", ")
    |> hello(language_code)
  end

  def hello(name, language_code) when is_binary(name) do
    phrase(language_code) <> name
  end

  defp phrase("en"), do: "Hello, "
  defp phrase("es"), do: "Hola, "
end

iex> Greeter.hello ["Sean", "Steve"]
"Hello, Sean, Steve"

iex> Greeter.hello ["Sean", "Steve"], "es"
"Hola, Sean, Steve"

書き直す例もあってありがたい(なんでこれでいけるのかアレだけどそういうものっていう認識をした)

パイプライン演算子

パイプライン演算子(|>)はある式の結果を別の式に1つ目の引数として渡します。

まってましたパイプライン演算子

導入

プログラミングは厄介になりえます。実際とても厄介なことに、関数呼び出しを深くしすぎると把握するのがとても難しくなります。以下のネストされた関数を考えてみてください:

foo(bar(baz(new_function(other_function()))))

これ!!!!講義でLispなどの関数言語触ったときにカッコ閉じを数えるのつらすぎて無理無理の無理になってた記憶 しかない

ここでは、other_function/0の値をnew_function/1に、new_function/1の値をbaz/1に、baz/1の値をbar/1に、そして最後にbar/1の結果をfoo/1に渡しています。 Elixirではパイプライン演算子を使うことによって構文的な混沌に対し現実的にアプローチします。 パイプライン演算子(|>)は 一つの式の結果を取り、それを渡します。先ほどのコードスニペットをパイプライン演算子で書き直すとどうなるか、見てみましょう。

other_function() |> new_function() |> baz() |> bar() |> foo()

パイプライン演算子は結果を左に取りそれを右側に渡します。

みやすい!優勝!!

この例のセットのためにElixirのStringモジュールを使います。

おおまかに文字列をトークン化する

iex> "Elixir rocks" |> String.split()
["Elixir", "rocks"]

全てのトークンを大文字にする

iex> "Elixir rocks" |> String.upcase() |> String.split()
["ELIXIR", "ROCKS"]

文字列の終わりを調べる

iex> "elixir" |> String.ends_with?("ixir")
true

ベストプラクティス

関数のアリティが1より多いなら括弧を使うようにしてください。括弧の有無はElixirにおいてはたいした問題ではありませんが、あなたのコードを誤解するかもしれない他のプログラマにとっては問題です。もし3つ目の例でString.ends_with?/2から括弧を削除すると, 以下のように警告されます。

iex> "elixir" |> String.ends_with? "ixir"
warning: parentheses are required when piping into a function call. For example:

  foo 1 |> bar 2 |> baz 3

is ambiguous and should be written as

  foo(1) |> bar(2) |> baz(3)

true

関数のカッコの有無ってわりと可読性的な意味で扱い難しい...

まとめ

  • 無名関数fn(a, b) -> a + b end
  • 無名関数省略記法 &(&1 + &2)
  • パターンマッチングによる関数の分岐がしゅごい
  • アリティ(引数の数)が違うと同名でも別の関数として扱われる
  • 関数の引数はパターンマッチングが用いられいる
  • プライベート関数はdefp
  • ガードを用いることで引数の型に基づいて使う関数を決められる def hoge(names) when is_list(names) do...
  • デフォルト引数は\\、ただしガードと組合わせるときは注意
  • 関数はネストしないでパイプライン演算子(|>)を使おう

なかなかElixirが便利な理由が見えてきましたね〜〜

関数型言語のコードってなかなか馴染みがなくて難しいです

プリッカソン#8に参加のかしこま

プリッカソン#8に参加してきました

prickathon.connpass.com

今回から前回のプリッカソン(#7)で塩キャベツ先生(@siokya)が描いてくださいったバナーに変更されました

らぁらちゃんかわいい

プリッカソンとは

要するにエンジニア女児による技術イベントですが、プリッカソン自体はコンテンツが好きなら誰でもウェルカム!雑談も含めてなんでもやってみよう!みたいなスタンスで優しい会となっています

みんな友達みんなアイドルの精神ですね

実際に会場にきて違うタスクをしながらプリティーシリーズについての会話をしてる方もいてフリーダムでした

日曜開催ということもあり、開始と同時に最新話を視聴しながら実況とかしてて楽しかったです

今回も主催の @takanakahiko さんはじめ運営の方ありがとうのゆめかわ!

当日

やる気元気寝起きの状態で自己紹介 → 交流・コンテンツ鑑賞しながらもくもく開発 → 成果発表 → 懇親会

という感じの流れでした

技術領域・年齢・性別もバラバラな集まりでしたが、プリティーシリーズ好きという共通のある人たちの集まりだったので、とても居心地がよかったです

発表もARで色々やってる方・イラスト描いていた方・スマホ向けのアプリケーション・WEBアプリケーション・slack用のアプリケーションなど多種多様でめちゃくちゃおもしろかったです!

やったこと

自分は前回に引き続きキャプ画像DBの開発を行いました

普段から手をつけておりすでに公開・運用済みだったので改良をしていました

実際主目的はキャプ画像DBの宣伝(?)だった気がしないでもない気がしました (あんまりうまく説明できなかったのが後悔)

pri-image-db.site

前回の記事は↓になります

nishikino3.hatenablog.com

前回から今回までの間で

  • マスタデータ(作品やキャラ)の登録や編集
  • 画像の投稿とデータとの紐付け
  • キャラと作品での検索
  • セリフでの検索
  • ソート
  • UX改善
  • 公開
  • Twitter連携

をやっており

今回のプリッカソンでは

  • 他の方が開発している PrismDB との接続を試みるということで話数の情報を引っこ抜いて画像に情報付与
  • 話数の情報で検索
  • slackで画像投稿を通知
  • 紹介ページの作成

をしました

アプリケーションの運用・開発なので細かいところで結構時間とられがちで大変です...

つかったもの

Ruby on Rails

基本的にすべての機能はRailsで実装しました

細かいところを見ていくと

  • DBはポスグレ
  • Twtter投稿にはRedisを使って定期的に非同期処理
  • フロントはSemantic UIというCSSフレームワーク

heroku

  • デプロイが楽ちん

S3

  • ファイル保存が楽ちん

今後

とにかく今は画像のアップロード作業がだるい!!!

ので画像のアップロードだけでも楽になるような仕組みを作りたいです

ひとまず次にやろうとしているのは

  • アップロードのAPI
  • 画像を一気にアップロードするスクリプトを組む

あたりかなという感じです フロントエンドの勉強がてらいい感じのUIにしていくのもやりたいです

個人的な究極のゴールは

  • 過去作品のおもしろそうなキャプ画をすべて情報込みで登録する!
  • 最新話放送されたら自動でキャプ画像が登録される
  • イケてるUI

お願い

キャプ画像持て余してる方いたらアップロードしていただけると嬉しいです!!!

http://pri-image-db.site/images/new

現状画像が300枚もないのでtwitterでよく同じ画像をみかけます笑

あとよかったらbotのtwiterをよろしくおねがいします!

twitter.com

最後に

会場を提供してくださいったGaiaxさん・素晴らしい会を運営していただいた なかひこくんさん、ありがとうございました!!

Gaiax一番すきな株式会社です!

Elixir勉強日記4 〜パターンマッチング・制御構造編〜

引き続きElixir勉強日記を書いていきます

ほんとは毎日できるといいんですけどうまくいかないです

毎日動画投稿しているYoutuberさんはすごいな 〜とか思う今日このごろです

f:id:nishikino3:20190611145254j:plain

elixirschool.com

今回はパターンマッチングと制御構造のところをまとめてやっていきをしていきます

そろそろ勉強を進めながらなにかしら作ってみたい気持ちになってきてますね

パターンマッチング

パターンマッチングはElixirの強力な部品で、単純な値やデータ構造、果ては関数でさえもマッチすることができます。 このレッスンではパターンマッチングの使い方から見ていきます。

強そう(小並)

マッチ演算子

Elixirでは、=演算子は実際には代数学での等号に値するマッチ演算子です。このマッチ演算子を通して値を代入し、その後マッチさせることができます。マッチングに成功すると方程式の結果を返します。失敗する場合はエラーを投げます。

iex> x = 1
1

一般的にプログラミング言語=って等号よりは代入(:=)を表すことが多いですがElixirにおいては代数学における等号に値するとあるので認識が違うみたいですね

上の例だと普通に代入しているように見えます

iex> 1 = x
1
iex> 2 = x
** (MatchError) no match of right hand side value: 1

あーーね

左辺に数字があるのは経験上気持ちわるいのですが、等号と同じという役割であるとなんとなく納得ができる感じがします

マッチ演算子はコレクションでも利用できます

# リスト
iex> list = [1, 2, 3]
iex> [1, 2, 3] = list
[1, 2, 3]
iex> [] = list
** (MatchError) no match of right hand side value: [1, 2, 3]

iex> [1 | tail] = list
[1, 2, 3]
iex> tail
[2, 3]
iex> [2|_] = list
** (MatchError) no match of right hand side value: [1, 2, 3]

# タプル
iex> {:ok, value} = {:ok, "Successful!"}
{:ok, "Successful!"}
iex> value
"Successful!"
iex> {:ok, value} = {:error}
** (MatchError) no match of right hand side value: {:error}

コレクションにおいても同様に使えるところをみるとコレクションを集合って考えたら代数学的に等号っていう表現がなんとなくわかるような気がします

ピン演算子

マッチ演算子は左辺に変数が含まれている時に代入操作を行います。 この変数を再び束縛するという挙動は望ましくない場合があります。 そうした状況のために、ピン演算子(^)があります。

ピン演算子で変数を固定すると、新しく再束縛するのではなく既存の値とマッチします。

変数定義・代入したときに使うのがマッチ演算子=で、マッチするか確認するのに使うのがピン演算子なんですかね

iex> x = 1
1
iex> ^x = 2
** (MatchError) no match of right hand side value: 2
iex> {x, ^x} = {2, 1}
{2, 1}
iex> x
2

上の例はピン演算子を使うともともと1が代入されているxと2はマッチしないんですがwwwって怒られてる感じですかね

上の例は分かるピン演算子で違う値に代入しようとしてMatchErrorを出しているからな

だが下の例はどういうことだアァァァァ!!??

落ち着いて考えるならマッチ演算子でタプルの左のxに2が代入されて、ピン演算子で1を代入しようとしたけど値を変える代入なので代入をやめたっていう認識をしました

Elixir 1.2ではマップのキーや関数の節でのピン演算子がサポートされました。

iex> key = "hello"
"hello"
iex> %{^key => value} = %{"hello" => "world"}
%{"hello" => "world"}
iex> value
"world"
iex> %{^key => value} = %{:hello => "world"}
** (MatchError) no match of right hand side value: %{hello: "world"}

この例がマップのキーを文字列からタプルに変えようとしているのに気づくまで1分かかりました

iex> greeting = "Hello"
"Hello"
iex> greet = fn
...>   (^greeting, name) -> "Hi #{name}"
...>   (greeting, name) -> "#{greeting}, #{name}"
...> end
#Function<12.54118792/2 in :erl_eval.expr/5>
iex> greet.("Hello", "Sean")
"Hi Sean"
iex> greet.("Mornin'", "Sean")
"Mornin', Sean"
iex> greeting
"Hello"

ファッ

関数内のgreetingと引数をピン演算子で確認して第一引数が"Hello"だったら"Hi"を表示してそれ以外の場合はそのまま表示してるって感じか

てかしれっと関数のマッチングしてそう

実際に動かすコードでこういう書き方ってするのか私気になります

制御構造

ifとunless

ひょっとすると以前にif/2と出くわしているかもしれませんし、Rubyを使っていればunless/2をご存知でしょう。Elixirではこの2つはほとんど同じように作用しますが、言語の構成要素としてではなく、マクロとして定義されています。この実装はKernel moduleで知ることができます。

Elixirでは偽とみなされる値はnilと真理値のfalseだけだということに、留意すべきです。

falsenilのみが偽となるのは基本的にはRubyと同様の制御がされるっぽいですね

言語の構成要素としてではなくマクロとして定義されているっていうあたりは、モジュールを呼び出す必要がないってことでいいんですかね

case

複数のパターンに対してマッチする必要があるなら、case/2を使うことができます

こちらもRubyと似たような使い方ができそうです

iex> case {:ok, "Hello World"} do
...>   {:ok, result} -> result
...>   {:error} -> "Uh oh!"
...>   _ -> "Catch all"
...> end
"Hello World"

あくまでも関数型言語なので分岐した先は関数っていう感覚で書くっていうのに慣れて生きていきたいです

_変数はcase/2命令文の中に含まれる重要な要素です。これが無いと、マッチするものが見あたらない場合にエラーが発生します

Rubyでいうところのdefaultってことなんすかね

iex> case :even do
...>   :odd -> "Odd"
...> end
** (CaseClauseError) no case clause matching: :even

iex> case :even do
...>   :odd -> "Odd"
...>   _ -> "Not Odd"
...> end
"Not Odd"

どうでもいいけどcase/2の引数に定数を渡すの気持ち悪いですね

_を”他の全て”にマッチするelseと考えましょう。

case/2はパターンマッチングに依存しているため、パターンマッチングと同じルールや制限が全て適用されます。既存の変数に対してマッチさせようという場合にはピン^演算子を使わなくてはいけません

iex> pie = 3.14
3.14
iex> case "cherry pie" do
...>   ^pie -> "Not so tasty"
...>   pie -> "I bet #{pie} is tasty"
...> end
"I bet cherry pie is tasty"

この辺り覚えてないと詰みかねないですね

case/2のもう1つの素晴らしい特徴として、ガード節に対応していることがあげられます

iex> case {1, 2, 3} do
...>   {1, x, 3} when x > 0 ->
...>     "Will match"
...>   _ ->
...>     "Won't match"
...> end
"Will match"

ガード節 分岐ができて いい感じ(575)

cond

値ではなく、条件をマッチさせる必要がある時には、cond/1を使うことができます。これは他の言語でいうところのelse ifやelsifのようなものです

引数のないcase/2みたいな認識でいいんすかね

iex> cond do
...>   2 + 2 == 5 ->
...>     "This will not be true"
...>   2 * 2 == 3 ->
...>     "Nor this"
...>   1 + 1 == 2 ->
...>     "But this will"
...> end
"But this will"

caseのように、condはマッチしない場合にエラーを発生させます。これに対処するには、trueになる条件を定義すればよいです

それはそう

with

特殊形式のwith/1はネストされたcase/2文を使うような時やきれいにパイプできない状況に便利です。with/1式はキーワード, ジェネレータ, そして式から成り立っています。

ジェネレータについてはリスト内包表記のレッスンでより詳しく述べますが、今は<-の右側と左側を比べるのにパターンマッチングが使われることを知っておくだけでよいです。

with/1の簡単な例から始め、その後さらなる例を見てみましょう

なんかややこしそうなやつですね

iex> user = %{first: "Sean", last: "Callan"}
%{first: "Sean", last: "Callan"}
iex> with {:ok, first} <- Map.fetch(user, :first),
...>      {:ok, last} <- Map.fetch(user, :last),
...>      do: last <> ", " <> first
"Callan, Sean"

これってuserのマップにfirstlastのkeyがあるか確認して表示してるってことなんすかね

式がマッチに失敗した場合はマッチしない値が返されます

iex> user = %{first: "doomspork"}
%{first: "doomspork"}
iex> with {:ok, first} <- Map.fetch(user, :first),
...>      {:ok, last} <- Map.fetch(user, :last),
...>      do: last <> ", " <> first
:error

ここではMap.fetch/2の戻り値が:errorなのでそいつが返ってきてるということですね

それでは、with/1を使わない長めの例と、それをどのようにリファクタリングできるかを見てみましょう

まずはcase/2で書いた場合ですね

case Repo.insert(changeset) do
  {:ok, user} ->
    case Guardian.encode_and_sign(user, :token, claims) do
      {:ok, jwt, full_claims} ->
        important_stuff(jwt, full_claims)

      error ->
        error
    end

  error ->
    error
end

それをwith/1に置き換えてこうじゃ!

with {:ok, user} <- Repo.insert(changeset),
     {:ok, jwt, full_claims} <- Guardian.encode_and_sign(user, :token, claims),
     do: important_stuff(jwt, full_claims)

優勝!!!wwww

Elixir 1.3からはwith/1でelseを使えます

import Integer

m = %{a: 1, c: 3}

a =
  with {:ok, number} <- Map.fetch(m, :a),
    true <- is_even(number) do
      IO.puts "#{number} divided by 2 is #{div(number, 2)}"
      :even
  else
    :error ->
      IO.puts("We don't have this item in map")
      :error

    _ ->
      IO.puts("It is odd")
      :odd
  end

これはcaseのようなパターンマッチングを提供することで、エラーを扱いやすくします。渡されるのはマッチングに失敗した最初の表現式の値です。

これってwith {:ok, number} <- Map.fetch(m, :a):errorだったらエラーを表示して:errorを返してるってことか!!!

天才!www

第4回まとめ

パターンマッチング

  • =(マッチ演算子)は代入というより等号を意味している.変数の再代入は可能
  • すでに定義されている変数に関しては^(ピン演算子)で値の確認ができる(等号
  • コレクションの要素毎の代入も可能

制御構造

  • if/2unless/2カーネルモジュールが与えている機能でおおよそRubyと同じ動作をする
  • case/2もおおよそRubyと同様に動作するがマッチングしない場合はdefaultではなく_
  • case/2のマッチングにはパターンマッチングと同じ制限が適用されるため既存の変数に対してはピン演算子を使う必要がある
  • cond/1else ifと似たような動作をする
  • case/2がネストしていたりする場合にはwith/1を使うといい感じにリファクタできる場合がある(errorのハンドラーとか)

Elixir特有の要素を掘り出していくとこれ使えるのかどうか疑問に思うことがある機能がある気がしますが、今回やったあたりをスマートに使いこなせるようになりたいですね

Elixir勉強日記3 〜コレクション・Enum編〜

Elixir勉強日記第3回です

f:id:nishikino3:20190611145254j:plain

今回もElixir Schoolを引き続きやっていきます

elixirschool.com

今回は基本的なデータ構造をまとめてきいます

メソッドの説明について

初めにメソッドの説明する際の記法に関して説明します

Elixir(とその土台のErlang)において、関数や演算子の名前は2つの部分、与えられた名前とその アリティ から成ります。アリティはElixir(とErlang)のコードについて説明するときの中核となるものです。アリティは関数や演算子が取る引数の数です。名前とアリティはスラッシュで繋げられます。後ほどより詳しく扱いますが、この知識は今のところこの表記法を理解する助けになるでしょう。

要するに(メソッド名)/(数字)という風に記述されていたらそのメソッド(メソッド名)の引数の個数(数字)ということになります

この(メソッド名)には演算子が入ることもあります(正確には演算子もメソッド)

Elixirではメソッドの引数の個数が異なると違うメソッドとして認識されるので気をつけましょう

リスト

リストは値の単純なコレクションで、複数の型を含むことができます。また、一意ではない値を含むことができます:

iex> [3.14, :pie, "Apple"]
[3.14, :pie, "Apple"]

同一の型である必要がないのはrubyと類似してますね

Elixirはリストコレクションを連結リストとして実装しています。すなわちリストの長さを得るのは線形時間(O(n))の処理となります。このことから、リスト先頭への追加はほとんどの場合にリスト末尾への追加より高速です:

ほうほうつまりリスト => 線形リストなので末尾への処理より先頭の処理の方が早いということですね

iex> list = [3.14, :pie, "Apple"]
[3.14, :pie, "Apple"]
# リスト先頭への追加(高速)
iex> ["π" | list]
["π", 3.14, :pie, "Apple"]
# リスト末尾への追加(低速)
iex> list ++ ["Cherry"]
[3.14, :pie, "Apple", "Cherry"]

リストの連結

リストの連結には++/2演算子を用います

iex> [1, 2] ++ [3, 4, 1]
[1, 2, 3, 4, 1]

文字列連結の演算子<>だったり、+では無かったりするので使い分けの注意が必要ですね

リストの減算

減算に対応するために--/2演算子が用意されています。存在しない値を引いてしまっても安全です

iex> ["foo", :bar, 42] -- [42, "bar"]
["foo", :bar]

べんり

重複した値に注意してください。右辺の要素のそれぞれに対し、左辺の要素のうち初めて登場した同じ値が順次削除されます

iex> [1,2,2,3,2,3] -- [1,2,3,2]
[2, 3]

はぇ

参考: リストの減算の値のマッチには strict comparison が使われています。

なるほど

頭部 / 尾部

iex> hd [3.14, :pie, "Apple"]
3.14
iex> tl [3.14, :pie, "Apple"]
[:pie, "Apple"]

# リストを頭部と尾部に分けるのにパターンマッチングやcons演算子(|)を使うこともできます
iex> [head | tail] = [3.14, :pie, "Apple"]
[3.14, :pie, "Apple"]
iex> head
3.14
iex> tail
[:pie, "Apple"]

タプル

タプルはリストに似ていますが、各要素はメモリ上に隣接して格納されます。このため、タプルの長さを得るのは高速ですが、修正を行うのは高コストとなります。というのも、新しいタプルは全ての要素がメモリにコピーされるからです。タプルは波括弧を用いて定義されます

タプルは関数から補助的な情報を返す仕組みとしてよく利用されます。この便利さは、パターンマッチングについて扱う時により明らかになるでしょう

iex> {3.14, :pie, "Apple"}
{3.14, :pie, "Apple"}
iex> File.read("path/to/existing/file")
{:ok, "... contents ..."}
iex> File.read("path/to/unknown/file")
{:error, :enoent}

書き換えが遅いってことは定数とかに使う感じなのでしょうかね

キーワードリスト

Elixirでは、キーワードリストは最初の要素がアトムのタプルからなる特別なリストで、リストと同様の性能になります

iex> [foo: "bar", hello: "world"]
[foo: "bar", hello: "world"]
iex> [{:foo, "bar"}, {:hello, "world"}]
[foo: "bar", hello: "world"]

キーワードリストの重要性は次の3つの特徴によって強調づけられています:

  • キーはアトムです。
  • キーは順序付けされています。
  • キーの一意性は保証されません。

連想配列のkeyをタプルに限定してリストと同じように記述するとリストと同様な性能が得られるということなんでしょうかね

こうした理由から、キーワードリストは関数にオプションを渡すために非常に良く用いられます。

なるほど

マップ

Elixirではマップは”花形の”キーバリューストアで、キーワードリストとは違ってどんな型のキーも使え、順序付けされません。マップは%{}構文で定義することができます:

花形で草

iex> map = %{:foo => "bar", "hello" => :world}
%{:foo => "bar", "hello" => :world}
iex> map[:foo]
"bar"
iex> map["hello"]
:world

# 変数をマップのキーにすることができます
iex> key = "hello"
"hello"
iex> %{key => "world"}
%{"hello" => "world"}

# アトムのキーだけを含んだマップには特別な構文があります
iex> %{foo: "bar", hello: "world"}
%{foo: "bar", hello: "world"}

iex> %{foo: "bar", hello: "world"} == %{:foo => "bar", :hello => "world"}
true

# アトムのキーにアクセスするための特別な構文もあります
iex> map = %{foo: "bar", hello: "world"}
%{foo: "bar", hello: "world"}
iex> map.hello
"world"

# マップのもう一つの興味深い特性は、マップの更新のための固有の構文があることです
iex> map = %{foo: "bar", hello: "world"}
%{foo: "bar", hello: "world"}
iex> %{map | foo: "baz"}
%{foo: "baz", hello: "world"}

マップの便利構文使いたかったらなるべくアトムをキーにしとけって気がした

%忘れそう

小まとめ

  • リスト
    • 単純なデータの羅列で複数の型に対応
    • [1, 2, "hoge"]
  • タプル
    • リストに類似するが、メモリ上に隣接して格納される.タプルを得るのは早いが修正を行うとすべてコピーしなおされため遅い
    • {1, 2, "hoge"}
  • キーワードリスト
    • アトムのタプルからなる特別なリストでリスト同様の性能
    • [foo: "bar", hello: "world"]
    • [{:foo, "bar"}, {:hello, "world"}]
  • マップ
    • どんな型でもキーにできるキーバリューストア
    • %{}

Enum

コレクションを列挙していくために用いる一連のアルゴリズム

コレクションを制すぞい

Enumモジュールはおよそ70個以上の関数を含んでいます。タプルを除外した全てのコレクションは全て列挙可能です。Enumモジュールはおよそ70個以上の関数を含んでいます。前回のレッスンで学習した、タプルを除外した全てのコレクションは全て列挙可能です。

めんどうなので雑に列挙します

all?

all?を使うとき、そしてEnumの多くのケースで、コレクションの要素に適用する関数を渡します。all?の場合には、コレクション全体でこの関数はtrueと評価されなければならず、これを満たさない場合はfalseが返ります。

全部条件に合うえばtrue

any?

any?は少なくとも1つの要素がtrueと評価された場合にtrueを返します:

一つでも条件に合えばtrue

chunk_every

コレクションを小さなグループに分割する必要があるなら、恐らくchunk_every/2こそが探し求めている関数でしょう

&2で指定した長さのコレクションに分割

chunk_by

コレクションを要素数ではない何か他のものでグループにする必要がある場合には、chunk_by/2関数を使うことができます。この関数は列挙可能な値と関数を引数に取り、その関数の返り値が変わると新しいグループが始まります

iex> Enum.chunk_by(["one", "two", "three", "four", "five"], fn(x) -> String.length(x) end)
[["one", "two"], ["three"], ["four", "five"]]
iex> Enum.chunk_by(["one", "two", "three", "four", "five", "six"], fn(x) -> String.length(x) end)
[["one", "two"], ["three"], ["four", "five"], ["six"]]

指定した方法でグルーピングできる

map_every

時にコレクションをグループに別けるだけでは充分ではない場合があります。nth毎のアイテムに対して何かの処理をしたい時にはmap_every/3が有用です。これは最初の要素にかならず触れます。

# 毎回3個を飛び越えながら関数を呼び出す
iex> Enum.map_every([1, 2, 3, 4, 5, 6, 7, 8], 3, fn x -> x + 1000 end)
[1001, 2, 3, 1004, 5, 6, 1007, 8]

一部の要素に対して処理したいときに使いそう

each

新しい値を生成することなく、コレクションを反復する必要があるかもしれません。こうした場合にはeach/2を使います

iex> Enum.each(["one", "two", "three"], fn(s) -> IO.puts(s) end)
one
two
three
:ok

map

関数を各要素に適用して新しいコレクションを生み出すには、map関数に目を向けましょう

iex> Enum.map([0, 1, 2, 3], fn(x) -> x - 1 end)
[-1, 0, 1, 2]

人生で一番使いそう

min

コレクションの中で最小の(min/1)値を探します

min/2も同様ですが、コレクションが空である場合、最小値を生成するための関数を渡します

max

minとおなじ

filter

filter/2を使用するとコレクションで与えられた関数で評価して true になる要素のみを返すことができます。

絞り込むときに絶対につかう

reduce

reduce/3を用いることで、コレクションをまとめ、そこから単一の値を抽出することができます。この処理を実行するにはオプションとしてアキュムレータ(積算器。この例では10)を関数に渡しますが、アキュムレータが与えられない場合にはコレクションの最初の値が用いられます:

iex> Enum.reduce([1, 2, 3], 10, fn(x, acc) -> x + acc end)
16

iex> Enum.reduce([1, 2, 3], fn(x, acc) -> x + acc end)
6

iex> Enum.reduce(["a","b","c"], "1", fn(x,acc)-> x <> acc end)
"cba1"

コレクションを一つにまとめるのね完全に理解した

rubyだとinjectが近そう

sort

コレクションをソートするのは1つではなく、2つあるソート関数を使えば簡単です。

sort/1Erlangのterm orderingを使ってソート順序を決めるというものです

これはシンプルに昇順になります

sort/2には順序決めに使う関数を渡すことができます

# ソート関数あり
iex> Enum.sort([%{:val => 4}, %{:val => 1}], fn(x, y) -> x[:val] > y[:val] end)
[%{val: 4}, %{val: 1}]

# なし
iex> Enum.sort([%{:count => 4}, %{:count => 1}])
[%{count: 1}, %{count: 4}]

これは必要

uniq_by

uniq_by/2を使ってコレクションから重複した要素を取り除くことができます

iex> Enum.uniq_by([1, 2, 3, 2, 1, 1, 1, 1, 1], fn x -> x end)
[1, 2, 3]

uniq/1もあったぷり

紹介されてる以外に使いそうなのはこんなところでしょうか

  • uniq/1
  • reverse/1
  • empty?/1
  • join/2
  • find/2
  • reject/2
  • with_index/1

ほかのメソッドたちは↓を見れば良さそう

hexdocs.pm

第3回まとめ

  • コレクション、リスト・キーワードリスト・タプル・マップについてまとめた
  • それぞれのコレクション長所や利点なんか生かして実装したい
  • Enumモジュールのメソッドを紹介した
  • いい感じに使えるようになりたい

キングオブプリズム プリズムワールド周りの事実確認と考察

※キンプリとプリティーリズムのネタバレが多く含まれてます

スッスッスッ10話までを視聴した方は分かると思うのですが、それまではキャラ回ではそれぞれがどのような理由でプリズムショーに向かい合っているか、という内容が主題でした

しかし4章では一転して、プリティーシリーズの世界全体の仕組みに関するストーリーが主軸となりました(一応それまでちらほら伏線は張られていた)

考察したいのですが色々と事実確認をしていかないと訳わからんになったので、プリズムワールド(プリティーリズムの世界)についてまとめて見ようと思います

プリズムの使者について

RLまでの情報

プリズムの使者についてレインボーライブの時点で分かっていることは

  • プリズムワールドから来たプリズムの煌めきを世界に広めるために世界に派遣される
  • 派遣される使者は中身が同一であるが異なる姿で世界にいることができる
  • 同じ世界に同時にプリズムの使者は1人しか存在できない(正確には複数人いると力が弱まる)
  • 各世界を移動すると記憶が失われる
  • 命は永遠である
  • 別の世界で伝説のジャンプを伝えアクトを導いた(あいら、みあヘインらしき絵とともに)
  • プリズムの使者は表舞台に立つと世界中がプリズムの煌めきで溢れてしまい滅亡へと向かってしまう
  • プリティーリズムレインボーライブにはりんねジュネの中身が同じ2人のプリズムの使者が同時に存在した
  • プリティーリズムレインボーライブの世界にはりんねが現れる5年前にジュネが現れる
  • ジュネは世界を離れるはずであったが、プリズムの女神によって残ることを許された。その代償にショーができなくなる・記憶を失う等のペナルティーを背負った

以上の情報はプリティーリズムレインボーライブの1,14,43,44,50話あたりを視聴すると分かります

キンプリ10話でわかったこと

キンプリ10話で分かったことについてまとめていきます

主にG.o.d.の会話と文字列を基にしています

  • プリズムの使者は初めはソナタプログラムが使用されていた
    • ver1.01からver2.26まであった
    • このソナタプログラムプリティーリズムの世界に関して多くの功績を残してきた
  • プリズムの煌めきが弱まったため、新たな試みとしてF型(Rinne ver1.01)M型(Shine ver1.01)のプリズムの使者を作り派遣
    • M型ヒビキワタルとして表舞台に出てキングになってしまう
    • F型は暴走したM型を止めるために封印する
  • M型封印後F型ver2.01~2.06...と自己消滅を繰り返す
  • その後不要な情報を削除し、最低限の情報のみを持ったRINNE ver3.01を派遣
    • その後RINNEプログラムは少なくともver4.11まで派遣される
  • F型の見た目だけを変え、メモリを引き継いだ新しいプログラムRuis ver1.01を派遣する
    • これがキンプリの世界に現れる如月ルヰである可能性が高い

ここでひとつレインボーライブでの内容の相違点があります

プリズムの使者は別の世界に移動すると記憶が失われる

しかし少なくともプリズムワールドの使者を派遣しているG.o.d.たちのメモリが云々の会話を聞く限り、任意に記憶が操作できることが可能であることが分かります

ソナタプログラムの成果

注意:このあたりから考察が混ざりだします

先程話したソナタプログラムの成果についてまとめていきます

これはG.o.d.たちがソナタプログラムについて話している際の映像の文字列をまとめます

  • Eve Hatsune Area:0001
    • Primogenitor of the Prismshow. Contributed to the propagation of The Sparkle of the Prism.
    • ハツネイヴ... 教祖みたいな感じなんですかね
  • Mikoto Kadowaki Area:0066
    • Created new concept values through Coordination.
    • カドワキミコト... プリパラの監督に似てる気がするなぁ...
  • Kanae Yamada Area:0100
    • The invention of the Prism Space and Prism Stone. Laid the foundations to spreading The Sparkle of the Prism.
    • きっとメガネかけててCV伊藤かな恵なんだろうなって気がする
  • Cosmo Hojo Area:0256
    • Global success as fashion designer. Conceived and spread advanced Coordination.
    • コズミックの人、RL・プリパラでちらほら出てた
  • Kei Takigawa Area:0400
    • Conceived Aurora Rising. Became the top standard for Stars around the world. Contributed throughout.Lifetime to the upbringing of Stars.
    • 唐突に窓開けて叫びだしてた人
  • Aira Harune Area:0476
    • Won Prism Queen. Succeeded in Aurora Rising Dream. Provided dreams to Stars around the world.
    • ぎゃふんの人の名前
  • Mia Ageha Area:0477
    • Completed Grateful Synphonia. Spread the Prism Act worldwide.
    • いっちぶぁぁんの人の名前
  • Maria Himuro Area:4989
    • Deceased after winning the Prism Queen. Moved countless people around the world.
    • 氷室聖の母親の名前、Area4989はキンプリの世界という言及はされている(12話)

この名前は使者なのか?

あくまでもこれらの名前成果として紹介されているだけであり、プリズムの使者自身を表している可能性もあり、プリズムの使者を派遣して得られた成果としてその世界の人間を表している可能性もあると考えられます

個人的には後者であってほしい気がしています

Areaに関して

ここではArea XXXXという項目がありこれは各世界を表している可能性があります

しかしこの考えは矛盾が一つあります

Area0400 0476 0477プリティーリズムオーロラドリーム及びディアマイフューチャーと同じ世界での出来事であるためArea自体が異なる世界だとするのは安直すぎます

なので以下のこと考えられます

  • そもそもAreaは世界を表していない
  • ソナタプログラムの功績と過去作の関連がそもそもない
    • RL14話だとADとDMFは別の世界のような表現をしていた
  • Areaの数値が近いと同じような世界が生成されている(シュタインズゲート世界線のイメージ)
  • 上位2桁が世界を表していてそれ以外の数字は別のものを表している
  • 派遣された使者によってナンバリングが変わる

シャインの封印に関して

  • Shinne ver1.01ヒビキワタルを宿主として派遣、その後Rinne ver1.01によって封印される
  • キンプリの世界で一条シンに宿り、シンがプリズムショーと如月ルヰ(Ruis ver1.01)に干渉していく内にShineが目覚める
  • 「封印されていた間も様々なプリズムスターを見てきた」というセリフからヒビキワタル一条シンの間にも様々な世界で封印されたまま宿主がいた可能性が高い
  • 目覚めかけたシャインはG.o.d.の使命でキンプラでルヰに再封印される。これによりシンはジャンプができなくなる。キングカップのシンのショーの途中でルヰが封印を解除し、シンはジャンプを成功させる
    • このときルヰから羽が消滅し、その後G.o.d.とのやり取りが無くなること、シンに対する「君と同じになれたね」というセリフからプリズムの使者を破棄したと考えられる
    • 後にショーができなくなるということから、プリズムの使者ながらも世界に残ることができたジュネと同じ状況と考えらる(ワンチャン記憶が無くなっている可能性も考えた)
  • スッスッス10話のprism oneで如月ルヰのショーはキングカップのときのような完璧なショーというよりはシンへの想いをそのまま表現したものになっており、ショーを再びできるようになったのは一条シンへの恋心による心の煌めきであると考えられる

レインボーライブのリンネとジュネ

レインボーライブのリンネはレインボーライブの世界でプリズムワールドに何らかの問題が発生し記憶を失って現れました (おそらく記憶を消して派遣されたため)

この時点で既に世界にプリズムの使者はジュネという形で存在していました

しかしジュネはプリズムクイーンになるなどして表舞台に立っていたため(プリズムの使者のタブー)、世界の煌めきを奪っていました

そのためどちらが世界に残るかを懸けてデュオショーをして決闘(最終的に言い争い)をします

結果どちらが勝ったか明確にされていませんでした。ショー直後の感じだと恋愛知ってるマウントでジュネが勝ったように見えたのですが、ペアチャムはリンネの方に現れたのでどちらが勝利なのかよく分かりません。

最終的にジュネは世界から消えかけますが、プリズムの女神にプリズムの使者を破棄することで特別に世界に残ることを許されます。

そしてプリズムの使者の使命を全うするためリンネは次の世界に行ってしまうのでした(ガチ泣きした)

このジュネがプリズムの使者を破棄されることでスッスッスッ10話の冒頭で出てきたプリズムの使者のエラーリストに含まれているのだと思われます

プリズムの使者のエラーリスト

Rinne Program Error List

Case20080714 Rinne 1.96
"Meguru Kisaki" Forbbiden Love

Case 20111114 Rinne2.54
"Ameri Hosho" Love addiction

Case20140329 Rinne 3.17
"June Amou" Triangle of behind Love

Program Disposed

この20140329は現実世界でのレインボーライブ最終回の放送日時と一致しています(他は謎)(一応調べたら20111114は現実世界のオーロラドリームの要の初登場回の3,4日前)

また、どのエラーリストを眺めると、プリズムの使者と恋愛がなんらかのトラブルになっているのかと思われます

ちなみに先程のアップデートの情報から見ると、こんな感じになります

  • Rinne 1.96はシャイン封印付近のリンネ
  • Rinne 2.54は自己消滅を繰り返す時期のリンネ
  • Rinne 3.17はRLの時期の数年前に派遣されたリンネ(ジュネ)

ヒビキワタルと山田リョウ

10話を視聴していると色々疑問を抱える人物、寮長の山田リョウと山田リョウの憧れのスタァヒビキワタルについてまとめます

前述した通りヒビキワタルはシャインに宿されていて、タブーである表舞台に立っており、キングカップ付近まで活躍しています

その後G.o.d.の命令で同時に派遣されたF型プログラムであるリンネによって封印されます

そしてヒビキワタル山田リョウ曰く「近所にいたプリズムスタァ」と言うセリフから実在していたことは明らかであるに関わらず、キンプリの世界ではヒビキワタルの情報は一切存在しておらず、他の誰の記憶にも残っていません

プリティーリズムでは山田・田中という人物が多く転生(?)していることから山田リョウも同様に転生している可能性もあるのでは...などと考えたりもしています

そう考えると、ヒビキワタル山田リョウがいた世界とキンプリの世界は異なる世界で山田リョウは記憶をもったまま転生してる可能性もあります(ソナタプログラムの功績の中にあるKanae Yamadaの可能性???)

最後に

読みにくくまとまっていない文章でしたがここまで読んでくださありがとうございました

正直考察はあんまり得意じゃないのですがプリティーシリーズが好きなので事実確認をメインに進めました

まだまだルヰやシャインが何回も口にしている1000年の時や、過去作とRL・キンプリで言及されているAD・DMFは同じ世界なのか...などまだまだ分からないことが多いのです

今回の考察や確認もこうだからこうっぽいって考えで進めることが多かったので、実際に正しいとは限らないということでご了承ください

今後またなにか新しい発見や気付きがあるといいです

Elixir勉強日記2 〜基本演算編〜

Elixir勉強日記(一日ずつやるとは言っていない)第2回です

f:id:nishikino3:20190611145254j:plain

今日は演算とかのあたりから進めていこうと思います

elixirschool.com

コードを見ながらざっくり進めていきいます

インストール

MacでもLinux(Ubuntu)でもさくっとインストールして動かせました

各所で議論される型ですね

言語によって重要視したりしてなかったりするのでElixirどんな感じなのか気になりますね

整数

# 動かしてみた感じ最大値は決まっていないようです
# あと長い桁数は_で区切ることもできます。この辺はrubyと似てますね
iex> 255
255
iex> 999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999

# 2進数、8進数、16進数は組み込みで対応しています
iex> 0b0110
6
iex> 0o644
420
iex> 0x1F
31

浮動小数

# Elixirでは、浮動小数点数は少なくとも1桁の数字とその後に続く小数を必要とし、 64ビットの倍精度で、指数eに対応しています
iex> 3.14
3.14
iex> .14 # この書き方はだめらしい
** (SyntaxError) iex:2: syntax error before: '.'
iex> 1.0e-10
1.0e-10
iex>  0.000000000000000000000000000001 + 1.0 
1.0

真理値

# Elixirは真理値としてtrueとfalseを提供しています。また、falseとnil以外は真とみなされます

iex> true
true
iex> false
false

アトム

# アトムは自身の名前がそのまま値になる定数です。Rubyをご存知なら、シンボルと同義になります
iex> :foo
:foo
iex> :foo == :bar
false

# 真理値のtrueとfalseはそれぞれ、アトムの:trueと:falseでもあります。
iex> true |> is_atom
true
# ちなみに!! |> を使うと左項の値を右項の引数として扱います!これすこ!シェルのパイプみたいな感じですね
iex> :true |> is_boolean
true
iex> :true === true
true

# Elixirのモジュールの名前もまたアトムです。MyApp.MyModuleは、そのようなモジュールが宣言されていなくても有効なアトムです。
iex> is_atom(MyApp.MyModule)
true

# アトムとモジュールの名前が被るのが若干懸念される気がしますね...

# アトムは、Erlangのビルトインのものも含めたライブラリのモジュールを参照するのにも使われます。
iex> :crypto.strong_rand_bytes 3
<<23, 104, 108>>

# アトムでErlangのモジュール参照できるのすごいですがわりと名前被りそうで怖い

文字列

# 文字列はElixirではUTF-8エンコードされていて、二重引用符で囲みます
iex> "Hello"
"Hello"
iex> "dziękuję"
"dziękuję"

# 文字列は改行やエスケープシーケンスに対応しています
iex> "foo
...> bar"
"foo\nbar"
iex> "foo\nbar"
"foo\nbar"

基本的な演算

算術

# 基本的にいつも通り
iex> 2 + 2
4
iex> 2 - 1
1
iex> 2 * 5
10
iex> 10 / 5 # /は常に浮動小数点を返すので注意
2.0

# 結果が整数の割り算
iex> div(10, 5)
2
# %ではないので気をつける
iex> rem(10, 3)
1

論理

# ||と&&と!は基本的にどんな型にも対応してる
iex> -20 || true
-20
iex> false || 42
42
iex> 42 && true
true
iex> 42 && nil
nil
iex> !42
false
iex> !false
true

# このあたりはrubyと同じですね
# nil判定とかに便利そう

# 最初の引数が真偽値である必要がある演算子`and``or``not`
iex> true and 42
42
iex> false or true
true
iex> not false
true
iex> 42 and true
** (ArgumentError) argument error: 42
iex> not 42
** (ArgumentError) argument error
# はぇ〜〜〜

比較

# 基本はrubyと一緒な感じ
iex> 1 > 2
false
iex> 1 != 2
true
iex> 2 == 2
true
iex> 2 <= 3
true
iex> 2 == 2.0
true
iex> 2 === 2.0 # 整数と小数を厳密に比較
false

# 型を比較できてソートにおいて有用
# これは他の言語ではあまり見られない気がします
# number < atom < reference < function < port < pid < tuple < map < list < bitstring

iex> :hello > 999
true
iex> {:hello, :world} > [1, 2, 3]
false
# これってリストを型でまとめるようにソートできるってことですかね?

文字列への式展開

# Rubyを使っているなら、Elixirでの式展開は見覚えがあるでしょう
iex> name = "Sean"
iex> "Hello #{name}"
"Hello Sean"

# 見覚えありますねぇ

文字列の連結

# 文字列連結は<>演算子を利用します
iex> name = "Sean"
iex> "Hello " <> name
"Hello Sean"

# +じゃないのね

第2回まとめ

  • 基本的な演算や数値の扱いはrubyと似ている(値自体がインスタンスではないけど)(そもそもElixirはクラスない?)
  • / は常に小数を返す
  • 剰余算は%じゃなくてrem/2
  • アトムはrubyのシンボルとほぼ同義
  • モジュール名はアトムとして扱われている
  • 論理演算は真偽値じゃなくても扱える
  • ただしand``or``not演算子は真偽値のみ
  • 比較は小数まで厳密に比較する===演算子があったり、異なる型だと型での比較ができる
  • 文字列の式展開もrubyと同じ、文字列連結の演算子<>

基本的な演算とかはrubyと同じで馴染みそうだな〜って感じです

Erlangのモジュール使うことあるしErlangも多少は眺めたほうがいいのかな

こっちも進めてみたい感じあるので気が向いたら切り替えるかもしれません

hexdocs.pm