LoginSignup
16
1

More than 3 years have passed since last update.

細かい話は置いといて、とりあえず触ってみたい人のための Elixir

Last updated at Posted at 2020-12-10

はじめに

この記事は、

fukuoka.ex Elixir/Phoenix Advent Calendar 2020 の 11日目です。

前日は @tuchiro さん

Phoenix LiveViewへのサービスリプレイスの際に既存コンテンツからの段階的な移行を考えてみる でした。

対象

  • プロゲート辺りを一通りやってみて、Ruby PHP JS 辺りを触ってみたよという段階の方
  • プログラミングElixir読んで速攻挫折しかかっている方
  • 中学生、高校生などで、環境構築について知見がないし、自分のPCもないよという方

環境

今回は環境構築も省きますので、paiza.io を使います。
プログラミングElixirではiexを使うので、環境構築が必要になりますが、そもそもそれも敷居が高いとか、ちょっと試してみたいだけ・・・という方もいらっしゃるはずです。
そういう方の為に朗報・・なんとpaiza.ioに Elixir Erlang があるんですよね。(スゴイ)

前提条件

文字列や数値、配列(リスト)連想配列(Hash)などのキーワードがある程度わかる方。

今回の目的

paizaのスキルチェックにElixirで挑戦出来るようになりたい。

1 まずは動かしてみましょう

1-1. paiza.ioを立ち上げてElixirを選択します

https://paiza.io リンクはここです。

Image from Gyazo

1-2. 最初のコードをかいてみましょう

こんなコードを書き込んでみて下さい。


x = "hello"
IO.puts x

で、四角で囲んだ「実行」ボタンを押すと・・

Image from Gyazo

下線部分のように下窓に実行結果が出てきます。

さて、ここで IO.puts を使っていますが、これを使うのはここまでです。

以降は、 IO.inspect を使います。

早速書き換えてみましょう。

x = "hello"
IO.inspect x

Image from Gyazo

IO.puts を使うと、 hello でしたが、 今回は、 "hello" となりました。

型が分かる、よりデバッグしやすい出力となります。

2. タプル

2-1. タプルって何?

タプルは、python 等を使われている方には馴染みかもしれませんが、そうでない方にはちょっと馴染みがない型となります。

{} で囲まれる型ですので、一瞬Hashとか連想配列的な何かと思いますが、Elixirのそれは、 %{} のように書きますので、別物となります。
また、キーバリューの形ではなく、配列(Elixirではリスト)の形にも似ています。

例えば、

{ "tokyo", "osaka", "fukuoka" }

や、

{ :tokyo, :osaka, :fukuoka }

のように書かれたりします。(前者は要素が文字列型、後者は要素がアトム型(シンボルみたいなモノ))

ちょっと試してみましょう。

x = { "tokyo", "osaka", "fukuoka" }
IO.inspect x

このように書いてみましょう。

Image from Gyazo

実行結果に {} で囲まれた複数の要素が確認できます。

ちなみに、 IO.puts x を使うと

Image from Gyazo

エラーが出ますのでご注意を。

2-2. タプルが使われている場所

Rails辺りしか知らなくて、「いけるやろー」とPhoenix を使うと割と困るのですが、(経験談) タプルは、ファイルを読み込む時とか、ライブラリを使ってApiの結果を取得する時とか、何かしらの結果を表示した時に

{ :ok, "何かしらの結果"}
{ :error, "何かしらの結果"}

みたいな感じで出力されるようです。

2-3. 値の取り出し方

まずは、通常考えられる取り出し方。

リスト同様キーがありませんので、数字を使います。

こんなコードを書いてみましょう。

prefectures = { :tokyo, :osaka, :fukuoka }
IO.inspect elem(prefectures, 0)
IO.inspect elem(prefectures, 1)
IO.inspect elem(prefectures, 2)

Image from Gyazo

と、elem/2 を使って書けます。

または、パターンマッチだと、

{ x, y, z } = { :tokyo, :osaka, :fukuoka }
IO.inspect x
IO.inspect y
IO.inspect z

こんな感じで取り出せます。

Image from Gyazo

ここでまた注意です。

{ x, y, z } = { :tokyo, :osaka, :fukuoka }
IO.inspect x

このように書くと、 y z が使われていないので、

Image from Gyazo

こんな、warning が出ます。そして、結果も出ません。

そういう場合は、

{ x, _, _z } = { :tokyo, :osaka, :fukuoka }
IO.inspect x

__z のようにアンダースコアを書くことで、パターンマッチから無視されるようになります。

パターンマッチに関しては、長くなるのでここでは割愛させていただきます。

2-4. File.read

簡単な使用例を見てみましょう。

わかりやすいのはファイルを読み込んだ時です。

Image from Gyazo

paiza.io は別ファイルを作る事も出来ますので、ファイルを増やしてリネームします。

以下のファイルを作って下さい。

test.txt

%{ :tokyo => "東京", :osaka => "大阪", :fukuoka => "福岡" }

Image from Gyazo

形としては、マップですが、取れるデーターは文字列となる想定です。(拡張子 exsでも同様の結果でした )

そして、 Main.exs の方はこのように書きます。

Main.exs

{ result, file } = File.read "test.txt"

IO.inspect(result)
IO.inspect(file)

実行結果は

Image from Gyazo

このようになります。

次に、ファイル名を存在しないものに変更します。

Main.exs

{ result, file } = File.read "hogehoge.txt"

IO.inspect(result)
IO.inspect(file)

すると、結果は、

Image from Gyazo

この通り、最初の要素で、 :ok または、 :error という結論を返してきて、 成功ならデーターを、 なければ :enoent を返しています。

enoent はファイルやディレクトリがないよ〜という意味のようです。

では、取れた結果を元にちょっとした加工をやってみましょう。

2-5. パイプ演算子

想定として、

「マップのつもりでデーター見たら文字列だったでござる」

というつもりですので、ここからパイプ演算子を使って加工していきます。

パイプ演算子については、細かい話は割愛しますが、ざっくり・・メソッドチェーンのような処理が可能になる点と、元のデーターを汚染しない点について認識していただければと思います。

{ result, file } = File.read "test.txt"

IO.inspect(result)

file
    |> String.replace(~r/"| |:|%{|}/, "")
    |> String.split(~r/,|=>/)
    |> Enum.chunk_every(2) 
    |> Enum.map(fn [k, v] -> {String.to_atom(k), v} end) 
    |> Map.new()

このような流れで考えてみます。

ただ、このまま実行すると経過が分からないので、

{ result, file } = File.read "test.txt"

IO.inspect(result)

file
    |> String.replace(~r/"| |:|%{|}/, "")
    |> IO.inspect()

    |> String.split(~r/,|=>/)
    |> IO.inspect()

    |> Enum.chunk_every(2) 
    |> IO.inspect()

    |> Enum.map(fn [k, v] -> { String.to_atom(k), v } end) 
    |> IO.inspect()

    |> Map.new()
    |> IO.inspect()

IO.inspect(file)

このように inspect を挟んでいきましょう。

Image from Gyazo

結果は見たとおり、 パイプで文字列を段階的に想定のデーターに加工ましたが、元の file に関しては最終行で出力した通り、無加工のものとなっています。

ちなみに・・ですが、パイプ演算子を使って関数を実行している時、本来の第一引数は、省略されています。例えば、

 file
   |> Map.new()

だとしたら、

 Map.new(file)

がそもそもの引数の入り方という事です。

これを連続した処理をするために、第一引数を先頭から引き継いだ形としてパイプ演算子を使って省略出来るようになっています。(なんかカッコいい)

3. リストとEnum.map

3-1. ゴニョゴニョするならリスト

タプルは、処理が早いそのものに要素を足したり引いたり、加工することには向いていないようです。

ですので、データーの加工に関してはリストで行う方が良いようです。

ここで、せっかくpaizaを利用しているので、スキルチェックに出てくるような入力を受け取って、任意の結果が出せるように考えてみましょう。

3-2. Enum.map

問題: ある三角形の三辺の値が改行区切りで入力されてきます。

その三角形が、

  • 正三角形の場合、「正三角形です」
  • 二等辺三角形の場合、「二等辺三角形です」
  • その他の場合、「三角形です」

と出力しましょう。

的な出題を解こうとした時、

paiza.io には入力を取る機能もありますので、

Image from Gyazo

こんな感じで、

2
2
4

と二等辺三角形となる値をセットしてみましょう。

そして、コードはこのように書いてみます。

data = [ nil, nil, nil ]
    |> Enum.map(fn s -> IO.gets(s) end)
    |> IO.inspect

    |> Enum.map(fn s -> String.trim(s) end)
    |> IO.inspect

    |> Enum.map(fn s -> String.to_integer(s) end)
    |> IO.inspect

    |> Enum.uniq
    |> IO.inspect

    |> Enum.count
    |> IO.inspect

result = fn
    1 -> "正三角形です"
    2 -> "二等辺三角形です"
    3 -> "三角形です"
end

IO.puts result.(data)

実行結果は、

Image from Gyazo

バッチリです。是非他の値も入れて遊んでみて下さい。

内容ですが、 IO.gets/1 で入力値を待ちますが、これを 三回分実行しています。
後は、 uniq で重複をまとめて要素の数をカウントすると、三角形の判断が出来ます。

パイプ演算子もカッコいいのですが、

result = fn
    1 -> "正三角形です"
    2 -> "二等辺三角形です"
    3 -> "三角形です"
end

この、関数の中に body を複数持たせて if を書いていない所が素敵ですよね。

ELixirは恐ろしい程 ループや条件式の記述が書かれないので、非常に面白いと感じますね。

これでpaiza序盤を少し遊ぶ事が出来るのではないでしょうか?

是非腕試ししてみて下さい!

最後に

ここまでご覧頂きましてありがとうございます。

初学者に向けたものとして、当記事のEnum辺りの処理の元となった piacere さんの記事も是非!(私自身がすごく勉強させていただきました。)

こちらは続き記事で Phoenix も扱いますよ。

Excelから関数型言語マスター1回目:データ行の”並べ替え”と”絞り込み”

そして、明日は @koyo-miyamura さんの記事です。

16
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
1