Elixir-quick-guide

提供:Dev Guides
移動先:案内検索

エリクサー-概要

Elixirは、スケーラブルで保守可能なアプリケーションを構築するために設計された動的で機能的な言語です。 低遅延、分散、フォールトトレラントシステムの実行で知られるErlang VMを活用し、Web開発や組み込みソフトウェアドメインでも使用されています。

Elixirは、ErlangとErlang VMの上に構築された機能的で動的な言語です。 Erlangは、配布、フォールトトレランス、同時実行性などのテレフォニーの問題を解決するために、1986年にエリクソンによって最初に書かれた言語です。 JoséValimによって書かれたElixirは、Erlangを拡張し、Erlang VMによりわかりやすい構文を提供します。 Erlangと同じレベルのパフォーマンスを維持しながらこれを行います。

Elixirの機能

Elixirのいくつかの重要な機能について説明しましょう-

  • スケーラビリティ-すべてのElixirコードは分離され、メッセージを介して情報を交換する軽量プロセス内で実行されます。
  • フォールトトレランス-Elixirは、問題が発生したときにシステムの一部を再起動し、動作が保証されている既知の初期状態に戻る方法を説明するスーパーバイザーを提供します。 これにより、アプリケーション/プラットフォームがダウンすることはありません。
  • 関数型プログラミング-関数型プログラミングは、開発者が短く、高速で、保守可能なコードを書くのに役立つコーディングスタイルを促進します。
  • ビルドツール-Elixirには一連の開発ツールが付属しています。 Mixは、プロジェクトの作成、タスクの管理、テストの実行などを簡単にするツールの1つです。 また、独自のパッケージマネージャー(Hex)もあります。
  • * Erlang互換性*-ElixirはErlang VM上で実行され、開発者はErlangのエコシステムに完全にアクセスできます。

Elixir-環境

Elixirを実行するには、システムでローカルにセットアップする必要があります。

Elixirをインストールするには、まずErlangが必要です。 一部のプラットフォームでは、ElixirパッケージにはErlangが含まれています。

Elixirのインストール

異なるオペレーティングシステムでのElixirのインストールについて理解しましょう。

Windowsセットアップ

ElixirをWindowsにインストールするには、https://repo.hex.pm/elixirwebsetup.exeからインストーラーをダウンロードし、[次へ]をクリックしてすべての手順を続行します。 ローカルシステムにインストールします。

インストール中に問題が発生した場合は、https://elixir-lang.org/installl [このページ]で詳細を確認できます。

Macセットアップ

Homebrewがインストールされている場合は、それが最新バージョンであることを確認してください。 更新するには、次のコマンドを使用します-

brew update

今、以下に示すコマンドを使用してElixirをインストールします-

brew install elixir

Ubuntu/Debianのセットアップ

Ubuntu/DebianセットアップでElixirをインストールする手順は次のとおりです-

Erlang Solutionsリポジトリを追加します−

wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb && sudo
dpkg -i erlang-solutions_1.0_all.deb
sudo apt-get update

Erlang/OTPプラットフォームとそのすべてのアプリケーションをインストールします-

sudo apt-get install esl-erlang

Elixirをインストールします−

sudo apt-get install elixir

その他のLinuxディストリビューション

他のLinuxディストリビューションがある場合は、https://elixir-lang.org/installl [このページ]にアクセスして、ローカルシステムにelixirを設定してください。

セットアップのテスト

システムでElixirセットアップをテストするには、ターミナルを開いてそこにiexと入力します。 それは次のようなインタラクティブなエリクサーシェルを開きます-

Erlang/OTP 19 [erts-8.0] [source-6dc93c1] [64-bit]
[smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]

Interactive Elixir (1.3.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>

これで、Elixirがシステムに正常にセットアップされました。

Elixir-基本的な構文

通常の「Hello World」プログラムから始めます。

Elixirインタラクティブシェルを起動するには、次のコマンドを入力します。

iex

シェルが起動したら、 IO.puts 関数を使用して、コンソール出力に文字列を「入力」します。 Elixirシェルに次のように入力します-

IO.puts "Hello world"

このチュートリアルでは、拡張子が .ex のファイルにElixirコードを保持するElixirスクリプトモードを使用します。 上記のコードを test.ex ファイルに保存します。 後続のステップでは、* elixirc *-を使用して実行します

IO.puts "Hello world"

次のように上記のプログラムを実行してみましょう-

$elixirc test.ex

上記のプログラムは、次の結果を生成します-

Hello World

ここでは、出力としてコンソールに文字列を生成するために、関数 IO.puts を呼び出しています。 この関数は、C、C ++、Javaなどで行うように呼び出すこともでき、関数名に続く括弧で引数を提供します-

IO.puts("Hello world")

コメント

単一行のコメントは「#」記号で始まります。 複数行のコメントはありませんが、複数のコメントを積み重ねることができます。 たとえば-

#This is a comment in Elixir

行末

「;」のような必須の行末記号はありません。エリクサーで。 ただし、「;」を使用して、同じ行に複数のステートメントを含めることができます。 例えば、

IO.puts("Hello"); IO.puts("World!")

上記のプログラムは、次の結果を生成します-

Hello
World!

識別子

変数、関数名などの識別子は、変数、関数などを識別するために使用されます。 Elixirでは、その後に数字、アンダースコア、大文字を含む小文字のアルファベットで識別子に名前を付けることができます。 この命名規則は、一般にsnake_caseとして知られています。 たとえば、次はElixirの有効な識別子です-

var1       variable_2      one_M0r3_variable

変数には、先頭にアンダースコアを付けることもできます。 使用することを意図していない値は、_またはアンダースコアで始まる変数に割り当てる必要があります-

_some_random_value = 42

また、エリクサーはアンダースコアに依存して、関数をモジュールにプライベートにします。 モジュールの先頭にアンダースコアを付けて関数に名前を付け、そのモジュールをインポートすると、この関数はインポートされません。

Elixirの関数の命名に関連する複雑さは他にもたくさんありますが、これについては次の章で説明します。

予約語

次の単語は予約されており、変数、モジュール、または関数名として使用できません。

after     and     catch     do     inbits     inlist     nil     else     end
not     or     false     fn     in     rescue     true     when     xor
__MODULE__    __FILE__    __DIR__    __ENV__    __CALLER__

Elixir-データ型

任意の言語を使用するには、その言語がサポートする基本的なデータ型を理解する必要があります。 この章では、エリクサー言語でサポートされている7つの基本的なデータ型、整数、浮動小数点、ブール、アトム、文字列、リスト、タプルについて説明します。

数値型

Elixirは、他のプログラミング言語と同様に、整数と浮動小数点の両方をサポートしています。 elixirシェルを開き、整数または浮動小数点数を入力として入力すると、その値が返されます。 例えば、

42

上記のプログラムが実行されると、次の結果が生成されます-

42

数字を8進数、16進数、2進数で定義することもできます。

オクタル

8進数で数値を定義するには、「0o」をプレフィックスとして付けます。 たとえば、8進数の0o52は10進数の42と同等です。

16進数

10進数で数値を定義するには、「0x」をプレフィックスとして付けます。 たとえば、16進数の0xF1は10進数の241と同等です。

バイナリ

バイナリベースで数値を定義するには、「0b」をプレフィックスとして付けます。 たとえば、バイナリの0b1101は、10進数の13と同等です。

Elixirは、浮動小数点数に対して64ビットの倍精度をサポートしています。 また、べき乗スタイルを使用して定義することもできます。 たとえば、10145230000は1.014523e10と記述できます

原子

原子は名前が値である定数です。 color(:)シンボルを使用して作成できます。 例えば、

:hello

ブール値

Elixirはブール値として true および false をサポートしています。 これらの値は両方とも、実際にはそれぞれ原子:trueおよび:falseに付加されています。

文字列

Elixirの文字列は二重引用符の間に挿入され、UTF-8でエンコードされます。 複数行にまたがり、補間を含めることができます。 文字列を定義するには、単に二重引用符で入力します-

"Hello world"

複数行の文字列を定義するには、三重の二重引用符でPythonに似た構文を使用します-

"""
Hello
World!
"""

文字列の章では、文字列、バイナリ、および文字列(文字列に類似)について詳しく学習します。

バイナリ

バイナリは、コンマで区切られた<< >>で囲まれたバイトのシーケンスです。 例えば、

<< 65, 68, 75>>

バイナリは、もしあれば、ビットとバイトに関連するデータを処理するために主に使用されます。 デフォルトでは、各値に0〜255を保存できます。 このサイズ制限は、その値を格納するために必要なビット数を示すサイズ関数を使用して増やすことができます。 例えば、

<<65, 255, 289::size(15)>>

リスト

Elixirは角括弧を使用して値のリストを指定します。 値は任意のタイプにすることができます。 例えば、

[1, "Hello", :an_atom, true]

リストには、リストの先頭と末尾をそれぞれ返すhdとtlという名前のリストの先頭と末尾の組み込み関数が付属しています。 リストを作成すると、charリストが返されることがあります。 これは、エリクサーが印刷可能なASCII文字のリストを見ると、それを文字リストとして印刷するためです。 文字列と文字リストは等しくないことに注意してください。 リストについては、後の章でさらに説明します。

タプル

Elixirは中括弧を使用してタプルを定義します。 リストと同様に、タプルは任意の値を保持できます。

{ 1, "Hello", :an_atom, true

ここで疑問が生じます-両方が同じように機能するのに、なぜ*リスト*と*タプル*の両方を提供するのですか? まあ、彼らは異なる実装を持っています。

  • リストは実際にはリンクリストとして保存されるため、リストへの挿入、削除は非常に高速です。
  • 一方、タプルは連続したメモリブロックに格納されるため、アクセスが高速になりますが、挿入と削除に追加コストがかかります。

Elixir-変数

変数は、プログラムが操作できる名前付きストレージを提供します。 Elixirの各変数には特定のタイプがあり、変数のメモリのサイズとレイアウトを決定します。そのメモリ内に保存できる値の範囲。変数に適用できる一連の操作。

変数の種類

Elixirは、次の基本的なタイプの変数をサポートしています。

整数

これらは整数に使用されます。 サイズは、32ビットアーキテクチャでは32ビット、64ビットアーキテクチャでは64ビットです。 整数は常にelixirで署名されます。 整数のサイズが制限を超えて拡大し始めると、エリクサーはそれを、メモリに収まる範囲で3〜nワードの範囲のメモリを占有するビッグ整数に変換します。

フロート

浮動小数点数は、エリクサーで64ビットの精度を持ちます。 メモリの点でも整数に似ています。 フロートを定義する場合、指数表記を使用できます。

ブール値

これらは、trueまたはfalseの2つの値をとることができます。

文字列

文字列は、エリクサーでエンコードされたutf-8です。 それらには、文字列を操作するための多くの機能をプログラマに提供する文字列モジュールがあります。

無名関数/ラムダ

これらは定義して変数に割り当てることができる関数で、この関数を呼び出すために使用できます。

コレクション

Elixirには多くのコレクションタイプがあります。 それらのいくつかは、リスト、タプル、マップ、バイナリなどです。 これらについては、以降の章で説明します。

可変宣言

変数宣言は、変数のストレージを作成する場所と量をインタープリターに伝えます。 Elixirでは、変数を宣言することはできません。 変数を宣言し、同時に値を割り当てる必要があります。 たとえば、lifeという名前の変数を作成して値42を割り当てるには、次のようにします-

life = 42

これにより、変数lifeが値42にバインドされます。 この変数に新しい値を再割り当てする場合、上記と同じ構文を使用してこれを行うことができます。

life = "Hello world"

変数の命名

変数の命名は、Elixirの snake_case 規則に従います。つまり、すべての変数は小文字で始まり、その後に0個以上の文字(大文字と小文字の両方)が続き、最後にオプションの「?」が続きます。または「!」。

変数名は先頭にアンダースコアを付けることもできますが、変数を無視する場合にのみ使用する必要があります。つまり、その変数は再び使用されることはありませんが、何かに割り当てる必要があります。

変数の印刷

対話型シェルでは、変数名を入力するだけで変数が出力されます。 たとえば、変数を作成する場合-

life = 42

そして、シェルに「life」と入力すると、出力が得られます-

42

ただし、変数をコンソールに出力する場合(ファイルから外部スクリプトを実行する場合)、変数を IO.puts 関数への入力として提供する必要があります-

life = 42
IO.puts life

or

life = 42
IO.puts(life)

これにより、次の出力が得られます-

42

Elixir-オペレーター

演算子は、特定の数学的または論理的な操作を実行するようコンパイラーに指示する記号です。 elixirが提供する多くの演算子があります。 彼らは次のカテゴリに分かれています-

  • 算術演算子
  • 比較演算子
  • ブール演算子
  • その他の演算子

算術演算子

次の表は、Elixir言語でサポートされているすべての算術演算子を示しています。 変数 A が10を保持し、変数 B が20を保持すると仮定します-

リンク:/elixir/elixir_example_arithematic [例を表示]

Operator Description Example
+ Adds 2 numbers. A + B will give 30
- Subtracts second number from first. A-B will give -10
* Multiplies two numbers. A*B will give 200
/ Divides first number from second. This casts the numbers in floats and gives a float result A/B will give 0.5.
div This function is used to get the quotient on division. div(10,20) will give 0
rem This function is used to get the remainder on division. rem(A, B) will give 10

比較演算子

Elixirの比較演算子は、他のほとんどの言語で提供されているものとほとんど共通しています。 次の表は、Elixirの比較演算子をまとめたものです。 変数 A が10を保持し、変数 B が20を保持すると仮定します-

リンク:/elixir/elixir_example_comparision [例を表示]

Operator Description Example
== Checks if value on left is equal to value on right(Type casts values if they are not the same type). A == B will give false
!= Checks if value on left is not equal to value on right. A != B will give true
=== Checks if type of value on left equals type of value on right, if yes then check the same for value. A === B will give false
!== Same as above but checks for inequality instead of equality. A !== B will give true
> Checks if the value of left operand is greater than the value of right operand; if yes, then the condition becomes true. A > B will give false
< Checks if the value of left operand is less than the value of right operand; if yes, then the condition becomes true. A < B will give true
>= Checks if the value of left operand is greater than or equal to the value of right operand; if yes, then the condition becomes true. A >= B will give false
Checks if the value of left operand is less than or equal to the value of right operand; if yes, then the condition becomes true. A ⇐ B will give true

論理演算子

Elixirは6つの論理演算子を提供します:and、or、not、&&、||および! 最初の3つの andまたはnot は厳密なブール演算子です。つまり、最初の引数がブールであることを期待しています。 非ブール引数はエラーを発生させます。 次の3つ、* &&、||および!は厳密ではないため、最初の値を厳密にブール値にする必要はありません。 それらは、厳密な対応物と同じように機能します。 変数 *A が真を保持し、変数 B が20を保持すると仮定します-

リンク:/elixir/elixir_example_logical [例を表示]

Operator Description Example
and Checks if both values provided are truthy, if yes then returns the value of second variable. (Logical and). A and B will give 20
or Checks if either value provided is truthy. Returns whichever value is truthy. Else returns false. (Logical or). A or B will give true
not Unary operator which inverts the value of given input. not A will give false
&& Non-strict and. Works same as *and *but does not expect first argument to be a Boolean. B && A will give 20
Non-strict* or*. Works same as *or *but does not expect first argument to be a Boolean. B
A will give true ! Non-strict* not*. Works same as not but does not expect the argument to be a Boolean.

注- and、_ or && および || _ ||短絡演算子です。 これは、 and の最初の引数がfalseの場合、2番目の引数をさらにチェックしないことを意味します。 or の最初の引数がtrueの場合、2番目の引数はチェックされません。 例えば、

false and raise("An error")
#This won't raise an error as raise function wont get executed because of short
#circuiting nature of and operator

ビット演算子

ビットごとの演算子はビットに作用し、ビットごとの操作を実行します。 Elixirはパッケージ Bitwise の一部としてビット単位モジュールを提供します。したがって、これらを使用するには、ビット単位モジュールを_使用_する必要があります。 それを使用するには、シェルで次のコマンドを入力します-

use Bitwise

次の例では、Aを5、Bを6と仮定します-

link:/elixir/elixir_example_bitwise [例を表示]

Operator Description Example
&&& Bitwise and operator copies a bit to result if it exists in both operands. A &&& B will give 4
Bitwise or operator copies a bit to result if it exists in either operand. A
B will give 7
>>> Bitwise right shift operator shifts first operand bits to the right by the number specified in second operand. A >>> B will give 0
<<< Bitwise left shift operator shifts first operand bits to the left by the number specified in second operand. A <<< B will give 320
^ Bitwise XOR operator copies a bit to result only if it is different on both operands. A ^ B will give 3
~ Unary bitwise not inverts the bits on the given number. ~A will give -6

その他の演算子

上記の演算子以外に、Elixirは Concatenation演算子、Match演算子、Pin演算子、Pipe演算子、String Match演算子、Code Point演算子、Capture演算子、Ternary Operator などの非常に強力な言語を提供します。

link:/elixir/elixir_example_misc [例を表示]

Elixir-パターンマッチング

パターンマッチングは、ElixirがErlangから継承する手法です。 これは、リスト、タプル、マップなどの複雑なデータ構造からより単純な下位構造を抽出できる非常に強力な手法です。

マッチには、2つの主要部分、*左*および*右*があります。 右側は、あらゆる種類のデータ構造です。 左側は、右側のデータ構造と一致させ、左側の変数を右側のそれぞれの下位構造にバインドしようとします。 一致が見つからない場合、オペレーターはエラーを発生させます。

最も単純な一致は、左側の孤立した変数と右側の任意のデータ構造です。 この変数は何にでも一致します。 例えば、

x = 12
x = "Hello"
IO.puts(x)

部分構造をキャプチャできるように、構造内に変数を配置できます。 例えば、

[var_1, _unused_var, var_2] = [{"First variable"}, 25, "Second variable" ]
IO.puts(var_1)
IO.puts(var_2)

これにより、値が保存されます、 \ {"最初の変数"} は_var_1_に、 "2番目の変数" は_var_2_に。 また、特別な '' 変数(または接頭辞が の変数)があります。これは、他の変数とまったく同じように機能しますが、エリクサーに指示します。 前の例では、__ unused_var_はそのような変数の1つでした。

この手法を使用して、より複雑なパターンを照合できます。 *例*では、リスト内にあるリスト内にあるタプル内の数値をアンラップして取得する場合、次のコマンドを使用できます-

[_, [_, {a}]] = ["Random string", [:an_atom, {24}]]
IO.puts(a)

上記のプログラムは、次の結果を生成します-

24

これにより、 a が24にバインドされます。 「_」を使用しているため、他の値は無視されます。

パターンマッチングでは、 right で変数を使用すると、その値が使用されます。 左側の変数の値を使用する場合は、ピン演算子を使用する必要があります。

たとえば、値25の変数「a」があり、値25の別の変数「b」と一致させる場合は、次のように入力する必要があります-

a = 25
b = 25
^a = b

最後の行は、 b の値に割り当てるのではなく、 a の現在の値と一致します。 一致しない左右のセットがある場合、一致演算子はエラーを発生させます。 たとえば、タプルとリスト、またはサイズ2のリストとサイズ3のリストを一致させようとすると、エラーが表示されます。

エリクサー-意思決定

意思決定構造では、プログラマーが、プログラムによって評価またはテストされる1つ以上の条件、および条件が true であると判断された場合に実行されるステートメント、およびオプションで次の場合に実行される他のステートメントを指定する必要があります条件は false と判断されます。

以下は、プログラミング言語のほとんどに見られる典型的な意思決定構造の一般的なものです-

意思決定

Elixirは、他の多くのプログラミング言語と同様にif/else条件構造を提供します。 また、見つかった最初の真の値を呼び出す cond ステートメントもあります。 Caseは、パターンマッチングを使用してプログラムのフローを制御する別の制御フローステートメントです。 それらを詳しく見てみましょう。

Elixirは、次のタイプの意思決定ステートメントを提供します。 詳細を確認するには、次のリンクをクリックしてください。

Sr.No. Statement & Description
1

if statement

ifステートメントは、ブール式とそれに続く do 、1つ以上の実行可能ステートメント、最後に end キーワードで構成されます。 ifステートメントのコードは、ブール条件がtrueと評価された場合にのみ実行されます。

2

if..else statement

ifステートメントの後に、オプションのelseステートメント(do..endブロック内)を続けることができます。これは、ブール式がfalseの場合に実行されます。

3

unless statement

exceptステートメントは、ifステートメントと同じ本体を持ちます。 exceptステートメント内のコードは、指定された条件がfalseの場合にのみ実行されます。

4

unless..else statement

except..elseステートメントの本体は、if..elseステートメントと同じです。 exceptステートメント内のコードは、指定された条件がfalseの場合にのみ実行されます。

5

cond

condステートメントは、いくつかの条件に基づいてコードを実行する場合に使用されます。 他のいくつかのプログラミング言語のif …​ else if….elseコンストラクトのような働きをします。

6

case

Caseステートメントは、命令型言語のswitchステートメントの代替と見なすことができます。 Caseは変数/リテラル​​を受け取り、さまざまなケースでパターンマッチングを適用します。 いずれかのケースが一致する場合、Elixirはそのケースに関連付けられたコードを実行し、caseステートメントを終了します。

エリクサー-弦

Elixirの文字列は二重引用符の間に挿入され、UTF-8でエンコードされます。 デフォルトの文字列がASCIIエンコードされ、256種類の文字しか使用できないCやC ++とは異なり、UTF-8は1,112,064コードポイントで構成されます。 これは、UTF-8エンコードがこれらの多くの異なる可能な文字で構成されていることを意味します。 文字列はutf-8を使用するため、ö、łなどの記号も使用できます。

文字列を作成する

文字列変数を作成するには、単に変数に文字列を割り当てます-

str = "Hello world"

これをコンソールに出力するには、単に IO.puts 関数を呼び出して、変数strを渡します-

str = str = "Hello world"
IO.puts(str)

上記のプログラムは、次の結果を生成します-

Hello World

空の文字列

文字列リテラル "" を使用して空の文字列を作成できます。 例えば、

a = ""
if String.length(a) === 0 do
   IO.puts("a is an empty string")
end

上記のプログラムは、次の結果を生成します。

a is an empty string

文字列補間

文字列補間は、定数、変数、リテラル、および式の組み合わせから新しい文字列値を構築する方法であり、その値を文字列リテラル内に含めます。 Elixirは、文字列内挿をサポートしています。文字列内の変数を使用する場合は、それを記述するときに中括弧で囲み、中括弧に '#' 記号を付加します。

例えば、

x = "Apocalypse"
y = "X-men #{x}"
IO.puts(y)

これはxの値を取り、それをyに置き換えます。 上記のコードは、次の結果を生成します-

X-men Apocalypse

文字列連結

前の章で文字列の連結の使用を見てきました。 '<>'演算子は、Elixirで文字列を連結するために使用されます。 2つの文字列を連結するには、

x = "Dark"
y = "Knight"
z = x <> " " <> y
IO.puts(z)

上記のコードは、次の結果を生成します-

Dark Knight

ストリングの長さ

文字列の長さを取得するには、 String.length 関数を使用します。 文字列をパラメーターとして渡すと、そのサイズが表示されます。 例えば、

IO.puts(String.length("Hello"))

上記のプログラムを実行すると、次の結果が生成されます-

5

文字列の反転

文字列を反転するには、それをString.reverse関数に渡します。 例えば、

IO.puts(String.reverse("Elixir"))

上記のプログラムは、次の結果を生成します-

rixilE

文字列比較

2つの文字列を比較するには、==または===演算子を使用できます。 例えば、

var_1 = "Hello world"
var_2 = "Hello Elixir"
if var_1 === var_2 do
   IO.puts("#{var_1} and #{var_2} are the same")
else
   IO.puts("#{var_1} and #{var_2} are not the same")
end

上記のプログラムは、次の結果を生成します-

Hello world and Hello elixir are not the same.

文字列マッチング

=〜文字列一致演算子の使用を見てきました。 文字列が正規表現に一致するかどうかを確認するには、文字列一致演算子またはString.match?を使用することもできます。 関数。 例えば、

IO.puts(String.match?("foo", ~r/foo/))
IO.puts(String.match?("bar", ~r/foo/))

上記のプログラムは、次の結果を生成します-

true
false

これは、=〜演算子を使用しても実現できます。 例えば、

IO.puts("foo" =~ ~r/foo/)

上記のプログラムは、次の結果を生成します-

true

文字列関数

Elixirは、文字列に関連する多数の関数をサポートしています。最も使用されるもののいくつかを次の表に示します。

Sr.No. Function and its Purpose
1

at(string, position)

指定されたutf8文字列の位置にある書記素を返します。 位置が文字列の長さより大きい場合、nilを返します

2

capitalize(string)

指定された文字列の最初の文字を大文字に変換し、残りを小文字に変換します

3

contains?(string, contents)

文字列に指定されたコンテンツが含まれているかどうかを確認します

4

downcase(string)

指定された文字列のすべての文字を小文字に変換します

5

ends_with?(string, suffixes)

指定されたサフィックスのいずれかで文字列が終了する場合、trueを返します

6

first(string)

utf8文字列から最初の書記素を返します。文字列が空の場合はnil

7

last(string)

utf8文字列から最後の書記素を返します。文字列が空の場合はnil

8

replace(subject, pattern, replacement, options \\ [])

件名のパターンの出現を置換で置換することによって作成された新しい文字列を返します

9

slice(string, start, len)

オフセットstartから始まり、長さlenの部分文字列を返します

10

split(string)

Unicodeの空白が出現するたびに文字列を部分文字列に分割し、先頭と末尾の空白を無視します。 空白のグループは、単一の出現として扱われます。 分割は、改行なしの空白には発生しません

11

upcase(string)

指定された文字列のすべての文字を大文字に変換します

バイナリ

バイナリは単なるバイトのシーケンスです。 バイナリは << >> を使用して定義されます。 例えば:

<< 0, 1, 2, 3 >>

もちろん、これらのバイトは、有効な文字列にならないシーケンスであっても、あらゆる方法で編成できます。 例えば、

<< 239, 191, 191 >>

文字列もバイナリです。 また、文字列連結演算子 <> は、実際にはバイナリ連結演算子です。

IO.puts(<< 0, 1 >> <> << 2, 3 >>)

上記のコードは、次の結果を生成します-

<< 0, 1, 2, 3 >>

ł文字に注意してください。 これはutf-8でエンコードされているため、この文字表現は2バイトを占有します。

バイナリで表される各数値は1バイトであるため、この値が255から上がると切り捨てられます。 これを防ぐために、サイズ修飾子を使用して、その数に必要なビット数を指定します。 たとえば-

IO.puts(<< 256 >>) # truncated, it'll print << 0 >>
IO.puts(<< 256 :: size(16) >>) #Takes 16 bits/2 bytes, will print << 1, 0 >>

上記のプログラムは、次の結果を生成します-

<< 0 >>
<< 1, 0 >>

utf8修飾子を使用することもできます。文字がコードポイントである場合、出力で生成されます。その他のバイト-

IO.puts(<< 256 :: utf8 >>)

上記のプログラムは、次の結果を生成します-

Ā

また、特定の変数がバイナリかどうかをチェックする is_binary という関数もあります。 8ビットの倍数として保存されている変数のみがバイナリであることに注意してください。

ビット列

サイズ修飾子を使用してバイナリを定義し、8の倍数ではない値を渡すと、バイナリではなくビット文字列になります。 例えば、

bs = << 1 :: size(1) >>
IO.puts(bs)
IO.puts(is_binary(bs))
IO.puts(is_bitstring(bs))

上記のプログラムは、次の結果を生成します-

<< 1::size(1) >>
false
true

これは、変数 bs がバイナリではなくビット文字列であることを意味します。 バイナリは、ビット数が8で割り切れるビット文字列であるとも言えます。 パターンマッチングは、バイナリとビット文字列で同じように機能します。

エリクサー-文字リスト

文字リストは、文字のリストにすぎません。 同じことを理解するには、次のプログラムを検討してください。

IO.puts('Hello')
IO.puts(is_list('Hello'))

上記のプログラムは、次の結果を生成します-

Hello
true

バイトを含む代わりに、charリストには単一引用符で囲まれた文字のコードポイントが含まれます。 そのため、二重引用符は文字列を表します(つまり、 バイナリ)、単一引用符は文字リストを表します(つまり、 リスト)。 IExは、いずれかの文字がASCII範囲外の場合、出力としてコードポイントのみを生成することに注意してください。

Charリストは、Erlang、特にバイナリを引数として受け入れない古いライブラリとインターフェイスするときに主に使用されます。 to_string(char_list)と_to_char_list(string)_関数を使用して、charリストを文字列に変換したり、逆に変換したりできます-

IO.puts(is_list(to_char_list("hełło")))
IO.puts(is_binary(to_string ('hełło')))

上記のプログラムは、次の結果を生成します-

true
true

-関数 to_string および to_char_list は多態性です。つまり、アトム、整数などの複数のタイプの入力を受け取り、それぞれ文字列および文字リストに変換できます。

Elixir-リストとタプル

(リンク)リスト

リンクリストは、メモリ内のさまざまな場所に格納されている要素の異種リストであり、参照を使用して追跡されます。 リンクリストは、関数型プログラミングで特に使用されるデータ構造です。

Elixirは角括弧を使用して値のリストを指定します。 値は任意のタイプにすることができます-

[1, 2, true, 3]

Elixirが印刷可能なASCII番号のリストを見ると、Elixirはそれを文字リスト(文字通り文字のリスト)として印刷します。 IExに値が表示され、その値が不明な場合は、 i 関数を使用してその情報を取得できます。

IO.puts([104, 101, 108, 108, 111])

リスト内の上記の文字はすべて印刷可能です。 上記のプログラムが実行されると、次の結果が生成されます-

hello

また、一重引用符を使用して、リストを他の方法で定義することもできます-

IO.puts(is_list('Hello'))

上記のプログラムが実行されると、次の結果が生成されます-

true

Elixirでは、シングルクォートとダブルクォートの表現は異なるタイプで表現されるため、同等ではありません。

リストの長さ

リストの長さを見つけるには、次のプログラムのように長さ関数を使用します-

IO.puts(length([1, 2, :true, "str"]))

上記のプログラムは、次の結果を生成します-

4

連結と減算

*++* および*-*演算子を使用して、2つのリストを連結および減算できます。 機能を理解するには、次の例を検討してください。
IO.puts([1, 2, 3] ++ [4, 5, 6])
IO.puts([1, true, 2, false, 3, true] -- [true, false])

これにより、最初のケースでは連結された文字列が、2番目のケースでは減算された文字列が得られます。 上記のプログラムは、次の結果を生成します-

[1, 2, 3, 4, 5, 6]
[1, 2, 3, true]

リストの頭と尾

ヘッドはリストの最初の要素で、テールはリストの残りの要素です。 それらは関数 hd および tl で取得できます。 リストを変数に割り当てて、その先頭と末尾を取得してみましょう。

list = [1, 2, 3]
IO.puts(hd(list))
IO.puts(tl(list))

これにより、リストの先頭と末尾が出力されます。 上記のプログラムは、次の結果を生成します-

1
[2, 3]

-空のリストの先頭または末尾を取得するとエラーになります。

その他のリスト関数

Elixir標準ライブラリは、リストを処理するための多くの機能を提供します。 ここでそれらのいくつかを見ていきます。 残りはhttp://elixir-lang.org/docs/stable/elixir/Listl[List]で確認できます。

S.no. Function Name and Description
1

delete(list, item)

リストから指定されたアイテムを削除します。 アイテムなしのリストを返します。 リスト内でアイテムが複数回出現する場合、最初に出現したものだけが削除されます。

2

delete_at(list, index)

指定されたインデックスの値を削除して、新しいリストを作成します。 負のインデックスは、リストの末尾からのオフセットを示します。 インデックスが範囲外の場合、元のリストが返されます。

3

first(list)

リストの最初の要素を返します。リストが空の場合はnilを返します。

4

flatten(list)

ネストされたリストの指定されたリストを平坦化します。

5

insert_at(list, index, value)

指定されたインデックスに値が挿入されたリストを返します。 インデックスはリストの長さに制限されていることに注意してください。 負のインデックスは、リストの末尾からのオフセットを示します。

6

last(list)

リストの最後の要素を返します。リストが空の場合はnilを返します。

タプル

タプルは、その中に他の多くの構造を格納するデータ構造でもあります。 リストとは異なり、メモリの連続したブロックに要素を保存します。 つまり、インデックスごとにタプル要素にアクセスしたり、タプルサイズを取得したりするのは高速な操作です。 インデックスはゼロから始まります。

Elixirは中括弧を使用してタプルを定義します。 リストのように、タプルは任意の値を保持できます-

{:ok, "hello"}

タプルの長さ

タプルの長さを取得するには、次のプログラムのように tuple_size 関数を使用します-

IO.puts(tuple_size({:ok, "hello"}))

上記のプログラムは、次の結果を生成します-

2

値を追加する

タプルに値を追加するには、Tuple.append関数を使用します-

tuple = {:ok, "Hello"}
Tuple.append(tuple, :world)

これは、新しいタプルを作成して返します:\ {:ok、 "Hello"、:world}

値を挿入する

特定の位置に値を挿入するには、 Tuple.insert_at 関数または put_elem 関数を使用できます。 同じことを理解するために、次の例を検討してください-

tuple = {:bar, :baz}
new_tuple_1 = Tuple.insert_at(tuple, 0, :foo)
new_tuple_2 = put_elem(tuple, 1, :foobar)
*put_elem* および *insert_at* が新しいタプルを返したことに注意してください。 Elixirデータ型は不変であるため、タプル変数に格納されている元のタプルは変更されませんでした。 Elixirコードは不変であるため、特定のコードがデータ構造を適切に変更しているかどうかを心配する必要がないため、推論が容易です。

タプルvs. リスト

リストとタプルの違いは何ですか?

リストはリンクリストとしてメモリに保存されます。つまり、リスト内の各要素は値を保持し、リストの最後に達するまで次の要素を指します。 値とポインターの各ペアをコンスセルと呼びます。 これは、リストの長さにアクセスすることは線形操作であることを意味します。サイズを計算するためにリスト全体を走査する必要があります。 リストの更新は、要素を追加している限り高速です。

一方、タプルはメモリに連続して格納されます。 これは、タプルサイズの取得またはインデックスによる要素へのアクセスが高速であることを意味します。 ただし、要素をタプルに更新または追加するには、メモリ内のタプル全体をコピーする必要があるため、コストがかかります。

Elixir-キーワードリスト

これまで、連想データ構造、つまり特定の値(または複数の値)をキーに関連付けることができるデータ構造については説明していません。 異なる言語は、辞書、ハッシュ、連想配列などの異なる名前でこれらの機能を呼び出します。

Elixirには、キーワードリストとマップという2つの主要な連想データ構造があります。 この章では、キーワードリストに焦点を当てます。

多くの関数型プログラミング言語では、連想データ構造の表現として2項目タプルのリストを使用するのが一般的です。 Elixirでは、タプルのリストとタプルの最初のアイテム(つまり、 キー)はアトムであり、キーワードリストと呼びます。 同じことを理解するために、次の例を検討してください-

list = [{:a, 1}, {:b, 2}]

Elixirは、このようなリストを定義するための特別な構文をサポートしています。 各アトムの最後にコロンを配置して、タプルを完全に取り除くことができます。 例えば、

list_1 = [{:a, 1}, {:b, 2}]
list_2 = [a: 1, b: 2]
IO.puts(list_1 == list_2)

上記のプログラムは、次の結果を生成します-

true

これらは両方ともキーワードリストを表します。 キーワードリストもリストであるため、それらのリストで使用したすべての操作を使用できます。

キーワードリスト内のアトムに関連付けられた値を取得するには、リストの名前の後に[]としてアトムを渡します-

list = [a: 1, b: 2]
IO.puts(list[:a])

上記のプログラムは、次の結果を生成します-

1

キーワードリストには3つの特別な特性があります-

  • キーはアトムでなければなりません。
  • キーは、開発者が指定した順序で並べられます。
  • キーは複数回指定できます。

Elixirはキーワードリストを操作するために、https://elixir-lang.org/docs/stable/elixir/Keywordl [キーワードモジュール]を提供しています。 ただし、キーワードリストは単なるリストであり、リストと同じ線形パフォーマンス特性を提供します。 リストが長いほど、キーの検索、アイテム数のカウントなどに時間がかかります。 このため、キーワードリストは主にオプションとしてElixirで使用されます。 多くのアイテムを保存する必要がある場合、または1つのキーに最大の1つの値を関連付ける必要がある場合は、代わりにマップを使用する必要があります。

キーへのアクセス

特定のキーに関連付けられた値にアクセスするには、 Keyword.get 関数を使用します。 指定されたキーに関連付けられた最初の値を返します。 すべての値を取得するには、Keyword.get_values関数を使用します。 たとえば-

kl = [a: 1, a: 2, b: 3]
IO.puts(Keyword.get(kl, :a))
IO.puts(Keyword.get_values(kl))

上記のプログラムは、次の結果を生成します-

1
[1, 2]

キーを挿入する

新しい値を追加するには、 Keyword.put_new を使用します。 キーが既に存在する場合、その値は変更されません-

kl = [a: 1, a: 2, b: 3]
kl_new = Keyword.put_new(kl, :c, 5)
IO.puts(Keyword.get(kl_new, :c))

上記のプログラムを実行すると、追加のキーcを含む新しいキーワードリストが生成され、次の結果が生成されます-

5

キーを削除する

キーのすべてのエントリを削除する場合は、 Keyword.delete; を使用してキーの最初のエントリのみを削除し、 Keyword.delete_first を使用します。

kl = [a: 1, a: 2, b: 3, c: 0]
kl = Keyword.delete_first(kl, :b)
kl = Keyword.delete(kl, :a)

IO.puts(Keyword.get(kl, :a))
IO.puts(Keyword.get(kl, :b))
IO.puts(Keyword.get(kl, :c))

これにより、リストの最初の b とリストのすべての a が削除されます。 上記のプログラムが実行されると、次の結果が生成されます-

0

Elixir-マップ

キーワードリストは、リストに保存されているコンテンツをキーでアドレス指定する便利な方法ですが、その下では、Elixirはまだリストを調べています。 リストをすべて調べる必要がある他の計画がある場合は適切かもしれませんが、データへの唯一のアプローチとしてキーを使用する予定の場合は不必要なオーバーヘッドになる可能性があります。

これは、地図があなたを救う場所です。 キーと値のストアが必要なときはいつでも、マップはElixirの「移動先」データ構造です。

地図を作成する

マップは%\ {}構文を使用して作成されます-

map = %{:a => 1, 2 => :b}

キーワードリストと比較して、我々はすでに2つの違いを見ることができます-

  • マップでは、任意の値をキーとして使用できます。
  • マップのキーは順序付けされません。

キーへのアクセス

キーに関連付けられた値にアクセスするために、マップはキーワードリストと同じ構文を使用します-

map = %{:a => 1, 2 => :b}
IO.puts(map[:a])
IO.puts(map[2])

上記のプログラムを実行すると、次の結果が生成されます-

1
b

キーを挿入する

キーをマップに挿入するには、マップ、新しいキー、および新しい値を引数として取る Dict.put_new 関数を使用します-

map = %{:a => 1, 2 => :b}
new_map = Dict.put_new(map, :new_val, "value")
IO.puts(new_map[:new_val])

これにより、キーと値のペア*:new_val-"value" *が新しいマップに挿入されます。 上記のプログラムを実行すると、次の結果が生成されます-

"value"

値の更新

マップに既に存在する値を更新するには、次の構文を使用できます-

map = %{:a => 1, 2 => :b}
new_map = %{ map | a: 25}
IO.puts(new_map[:a])

上記のプログラムを実行すると、次の結果が生成されます-

25

パターンマッチング

キーワードリストとは対照的に、マップはパターンマッチングに非常に役立ちます。 マップがパターンで使用されている場合、それは常に指定された値のサブセットに一致します-

%{:a => a} = %{:a => 1, 2 => :b}
IO.puts(a)

上記のプログラムは、次の結果を生成します-

1

これは、 a1 を一致させます。 したがって、出力は 1 として生成されます。

上記のように、マップは、パターン内のキーが特定のマップに存在する限り一致します。 したがって、空のマップはすべてのマップに一致します。

マップキーへのアクセス、マッチング、および追加時に変数を使用できます-

n = 1
map = %{n => :one}
%{^n => :one} = %{1 => :one, 2 => :two, 3 => :three}

https://elixir-lang.org/docs/stable/elixir/Mapl [マップモジュール]は、マップを操作するための便利な関数を備えたキーワードモジュールと非常によく似たAPIを提供します。 Map.get、Map.delete などの関数を使用して、マップを操作できます。

Atomキーを使用したマップ

マップにはいくつかの興味深いプロパティがあります。 マップ内のすべてのキーがアトムである場合、便宜上、キーワード構文を使用できます-

map = %{:a => 1, 2 => :b}
IO.puts(map.a)

マップの別の興味深い特性は、原子キーを更新およびアクセスするための独自の構文を提供することです-

map = %{:a => 1, 2 => :b}
IO.puts(map.a)

上記のプログラムは、次の結果を生成します-

1

この方法でアトムキーにアクセスするには、アトムキーが存在する必要があります。そうしないと、プログラムが動作しなくなります。

Elixir-モジュール

Elixirでは、いくつかの機能をモジュールにグループ化します。 前の章では、Stringモジュール、Bitwiseモジュール、Tupleモジュールなどのさまざまなモジュールを既に使用しています。

Elixirで独自のモジュールを作成するには、 defmodule マクロを使用します。 私たちは def マクロを使用してそのモジュールの機能を定義します-

defmodule Math do
   def sum(a, b) do
      a + b
   end
end

次のセクションでは、例のサイズが長くなるため、すべてをシェルに入力するのは難しい場合があります。 Elixirコードをコンパイルする方法と、Elixirスクリプトを実行する方法を学ぶ必要があります。

編集

モジュールをコンパイルして再利用できるように、モジュールをファイルに書き込むと常に便利です。 私たちは次の内容を持つmath.exという名前のファイルがあると仮定しましょう-

defmodule Math do
   def sum(a, b) do
      a + b
   end
end

コマンド-* elixirc *を使用してファイルをコンパイルできます。

$ elixirc math.ex

これにより、定義されたモジュールのバイトコードを含む Elixir.Math.beam という名前のファイルが生成されます。 iex を再び起動すると、モジュール定義が使用可能になります(iexがバイトコードファイルと同じディレクトリで起動される場合)。 例えば、

IO.puts(Math.sum(1, 2))

上記のプログラムは、次の結果を生成します-

3

スクリプトモード

Elixirファイル拡張子 .ex に加えて、Elixirはスクリプト用の .exs ファイルもサポートしています。 Elixirは両方のファイルをまったく同じように扱いますが、唯一の違いは目的です。 .ex ファイルはコンパイルされるためのもので、.exsファイルは scripting に使用されます。 実行されると、両方の拡張機能がモジュールをコンパイルしてメモリにロードしますが、*。ex *ファイルのみが.beamファイルの形式でディスクにバイトコードを書き込みます。

たとえば、同じファイルで Math.sum を実行したい場合、次の方法で.exsを使用できます-

Math.exs

defmodule Math do
   def sum(a, b) do
      a + b
   end
end
IO.puts(Math.sum(1, 2))

Elixirコマンドを使用して実行できます-

$ elixir math.exs

上記のプログラムは、次の結果を生成します-

3

ファイルはメモリにコンパイルされて実行され、結果として「3」が出力されます。 バイトコードファイルは作成されません。

モジュールのネスト

モジュールはElixirにネストできます。 この言語の機能は、コードをより良い方法で整理するのに役立ちます。 ネストされたモジュールを作成するには、次の構文を使用します-

defmodule Foo do
   #Foo module code here
   defmodule Bar do
      #Bar module code here
   end
end

上記の例では、 FooFoo.Bar の2つのモジュールを定義します。 2番目は、同じレキシカルスコープ内にある限り、 Foo 内で Bar としてアクセスできます。 後で、 Bar モジュールをFooモジュール定義の外に移動する場合は、フルネーム(Foo.Bar)で参照するか、エイリアスの章で説明したエイリアスディレクティブを使用してエイリアスを設定する必要があります。

-Elixirでは、言語がすべてのモジュール名をアトムに変換するため、Foo.Barモジュールを定義するためにFooモジュールを定義する必要はありません。 チェーン内のモジュールを定義せずに、任意にネストされたモジュールを定義できます。 たとえば、 Foo または Foo.Bar を定義せずに Foo.Bar.Baz を定義できます。

エリクサー-エイリアス

ソフトウェアの再利用を容易にするために、Elixirは3つのディレクティブを提供します- alias、require および import 。 また、以下に要約されているuseと呼ばれるマクロを提供します-

# Alias the module so it can be called as Bar instead of Foo.Bar
alias Foo.Bar, as: Bar

# Ensure the module is compiled and available (usually for macros)
require Foo

# Import functions from Foo so they can be called without the `Foo.` prefix
import Foo

# Invokes the custom code defined in Foo as an extension point
use Foo

次に、各ディレクティブについて詳しく理解しましょう。

エイリアス

エイリアスディレクティブを使用すると、任意のモジュール名のエイリアスを設定できます。 たとえば、ストリングモジュールにエイリアス 'Str' を指定する場合、次のように書くことができます-

alias String, as: Str
IO.puts(Str.length("Hello"))

上記のプログラムは、次の結果を生成します-

5

エイリアスは String モジュールに Str として与えられます。 これで、Strリテラルを使用して関数を呼び出すと、実際には String モジュールを参照します。 これは、非常に長いモジュール名を使用しており、現在のスコープ内で短いモジュール名に置き換える場合に非常に役立ちます。

注意-エイリアスは大文字で始まる必要があります。

エイリアスは、呼び出された lexical scope 内でのみ有効です。 たとえば、ファイルに2つのモジュールがあり、モジュールの1つ内でエイリアスを作成した場合、2番目のモジュールではそのエイリアスにアクセスできません。

組み込みモジュールにアクセスするために、ストリングやタプルなどの組み込みモジュールの名前を他のモジュールのエイリアスとして指定する場合、 "Elixir。" を先頭に追加する必要があります。 例えば、

alias List, as: String
#Now when we use String we are actually using List.
#To use the string module:
IO.puts(Elixir.String.length("Hello"))

上記のプログラムを実行すると、次の結果が生成されます-

5

要求する

Elixirは、メタプログラミング(コードを生成するコードの記述)のメカニズムとしてマクロを提供します。

マクロは、コンパイル時に実行および展開されるコードの塊です。 つまり、マクロを使用するには、コンパイル中にそのモジュールと実装が使用可能であることを保証する必要があります。 これは require ディレクティブで行われます。

Integer.is_odd(3)

上記のプログラムが実行されると、次の結果が生成されます-

** (CompileError) iex:1: you must require Integer before invoking the macro Integer.is_odd/1

Elixirでは、 Integer.is_odd は*マクロ*として定義されています。 このマクロはガードとして使用できます。 つまり、 Integer.is_odd を呼び出すには、Integerモジュールが必要です。

*require Integer* 関数を使用して、以下に示すようにプログラムを実行します。
require Integer
Integer.is_odd(3)

今回は、プログラムが実行され、 true として出力が生成されます。

一般に、モジュールは、そのモジュールで使用可能なマクロを使用する場合を除き、使用する前に必要ありません。 ロードされなかったマクロを呼び出そうとすると、エラーが発生します。 エイリアスディレクティブと同様に、_requireもレキシカルスコープであることに注意してください。 マクロについては、後の章で詳しく説明します。

インポート

*import* ディレクティブを使用して、完全修飾名を使用せずに他のモジュールの関数またはマクロに簡単にアクセスします。 たとえば、Listモジュールの *duplicate* 関数を数回使用する場合、単純にインポートできます。
import List, only: [duplicate: 2]

この場合、リストから重複した関数(引数リストの長さ2)のみをインポートしています。 *:only *はオプションですが、指定されたモジュールのすべての機能を名前空間内にインポートしないようにするために、その使用をお勧めします。 *:except *は、関数のリストを除くモジュール内のすべてをインポートするためのオプションとして指定することもできます。

*import* ディレクティブは、*:macros *および*:functions *を*:only *に与えることもサポートしています。 たとえば、すべてのマクロをインポートするには、ユーザーが書くことができます-
import Integer, only: :macros

importもrequireおよびaliasディレクティブと同じように* Lexicallyスコープ*されていることに注意してください。 また、モジュールを「インポート」することも「必須」であることに注意してください*。

use

ディレクティブではありませんが、 userequire と密接に関連するマクロであり、現在のコンテキストでモジュールを使用できます。 useマクロは、外部機能を現在の字句スコープ(多くの場合モジュール)に取り込むために、開発者によって頻繁に使用されます。 例を通してuseディレクティブを理解しましょう-

defmodule Example do
   use Feature, option: :value
end

使用は、上記を変換するマクロです-

defmodule Example do
   require Feature
   Feature.__using__(option: :value)
end
*use Module* は最初にモジュールを必要とし、次にModuleで *__ using __* マクロを呼び出します。 Elixirには優れたメタプログラミング機能があり、コンパイル時にコードを生成するマクロがあります。 上記のインスタンスで___using___マクロが呼び出され、ローカルコンテキストにコードが挿入されます。 ローカルコンテキストは、コンパイル時に_use macro_が呼び出された場所です。

Elixir-関数

関数は、特定のタスクを実行するためにまとめられた一連のステートメントです。 プログラミングの関数は、主に数学の関数のように機能します。 関数に入力を与えると、提供された入力に基づいて出力が生成されます。

Elixirには2種類の機能があります-

無名関数

  • fn..end構文を使用して定義された関数は、匿名関数です。 これらの関数は、ラムダとも呼ばれることがあります。 それらは変数名に割り当てて使用されます。

名前付き関数

  • defキーワード*を使用して定義された関数は、名前付き関数です。 これらはElixirで提供されるネイティブ関数です。

無名関数

名前が示すように、匿名関数には名前がありません。 これらは他の機能に頻繁に渡されます。 Elixirで匿名関数を定義するには、 fn および end キーワードが必要です。 これらの中で、*→ *で区切られた任意の数のパラメーターと関数本体を定義できます。 例えば、

sum = fn (a, b) -> a + b end
IO.puts(sum.(1, 5))

上記のプログラムを実行すると、実行され、次の結果が生成されます-

6

これらの関数は、名前付き関数のように呼び出されないことに注意してください。 関数名とその引数の間には「」があります。

キャプチャー演算子の使用

キャプチャ演算子を使用してこれらの関数を定義することもできます。 これは、関数を作成する簡単な方法です。 ここで、キャプチャ演算子を使用して上記の合計関数を定義します。

sum = &(&1 + &2)
IO.puts(sum.(1, 2))

上記のプログラムを実行すると、次の結果が生成されます-

3

簡略版では、パラメーターには名前が付けられていませんが、&1、&2、&3などとして使用できます。

パターンマッチング関数

パターンマッチングは、変数とデータ構造だけに限定されません。 パターンマッチングを使用して、関数をポリモーフィックにすることができます。 たとえば、1つまたは2つの入力(タプル内)を取り、それらをコンソールに出力できる関数を宣言します。

handle_result = fn
   {var1} -> IO.puts("#{var1} found in a tuple!")
   {var_2, var_3} -> IO.puts("#{var_2} and #{var_3} found!")
end
handle_result.({"Hey people"})
handle_result.({"Hello", "World"})

上記のプログラムが実行されると、次の結果が生成されます-

Hey people found in a tuple!
Hello and World found!

名前付き関数

関数を名前で定義して、後で簡単に参照できるようにすることができます。 名前付き関数は、defキーワードを使用してモジュール内で定義されます。 名前付き関数は常にモジュールで定義されます。 名前付き関数を呼び出すには、モジュール名を使用してそれらを参照する必要があります。

以下は、名前付き関数の構文です-

def function_name(argument_1, argument_2) do
   #code to be executed when function is called
end

Mathモジュール内で名前付き関数sumを定義しましょう。

defmodule Math do
   def sum(a, b) do
      a + b
   end
end

IO.puts(Math.sum(5, 6))

上記のプログラムを実行すると、次の結果が生成されます-

11

1行関数の場合、* do:*を使用して、これらの関数を定義するための簡略表記法があります。 たとえば-

defmodule Math do
   def sum(a, b), do: a + b
end
IO.puts(Math.sum(5, 6))

上記のプログラムを実行すると、次の結果が生成されます-

11

プライベート機能

Elixirは、定義されているモジュール内からアクセスできるプライベート関数を定義する機能を提供します。 プライベート関数を定義するには、 def の代わりに defp を使用します。 例えば、

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

Greeter.hello("world")

上記のプログラムが実行されると、次の結果が生成されます-

Hello world

ただし、* Greeter.phrase()*関数を使用してフレーズ関数を明示的に呼び出そうとすると、エラーが発生します。

デフォルト引数

引数のデフォルト値が必要な場合は、 argument \\ value 構文を使用します-

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

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

Greeter.hello("Ayush", "en")
Greeter.hello("Ayush")
Greeter.hello("Ayush", "es")

上記のプログラムが実行されると、次の結果が生成されます-

Hello, Ayush
Hello, Ayush
Hola, Ayush

エリクサー-再帰

再帰は、問題の解決策が同じ問題の小さなインスタンスの解決策に依存する方法です。 ほとんどのコンピュータープログラミング言語は、関数がプログラムテキスト内で自身を呼び出すことを許可することにより、再帰をサポートしています。

理想的には、再帰関数には終了条件があります。 この終了条件は、ベースケースとも呼ばれ、関数の再入力と関数呼び出しのスタックへの追加を停止します。 これは、再帰関数呼び出しが停止する場所です。 再帰関数をさらに理解するために、次の例を考えてみましょう。

defmodule Math do
   def fact(res, num) do
   if num === 1 do
      res
   else
      new_res = res * num
      fact(new_res, num-1)
      end
   end
end

IO.puts(Math.fact(1,5))

上記のプログラムを実行すると、次の結果が生成されます-

120

したがって、上記の関数 Math.fact では、数値の階乗を計算しています。 関数自体を呼び出していることに注意してください。 これがどのように機能するかを理解しましょう。

1と、階乗を計算する数値を提供しました。 この関数は、数値が1かどうかを確認し、1 (終了条件)の場合にresを返します。 そうでない場合は、変数new_resを作成し、以前のres * current numの値を割り当てます。 関数呼び出し_fact(new_res、num-1)_によって返された値を返します。 これは、numが1になるまで繰り返されます。 それが起こると、結果が得られます。

リストの各要素を1つずつ印刷する別の例を考えてみましょう。 これを行うには、リストの hd および tl 関数と関数のパターンマッチングを使用します-

a = ["Hey", 100, 452, :true, "People"]
defmodule ListPrint do
   def print([]) do
   end
   def print([head | tail]) do
      IO.puts(head)
      print(tail)
   end
end

ListPrint.print(a)

最初の印刷関数は、空のリスト(終了条件)があるときに呼び出されます。 そうでない場合は、2番目の印刷関数が呼び出され、リストが2に分割され、リストの最初の要素が先頭に、リストの残りの要素が末尾に割り当てられます。 次に、ヘッドが印刷され、リストの残りの部分、つまり末尾で再度print関数を呼び出します。 上記のプログラムが実行されると、次の結果が生成されます-

Hey
100
452
true
People

エリクサー-ループ

不変性のため、Elixirのループは(関数型プログラミング言語のように)命令型言語とは異なる方法で記述されています。 たとえば、Cのような命令型言語では、次のように記述します-

for(i = 0; i < 10; i++) {
   printf("%d", array[i]);
}

上記の例では、配列と変数 i の両方を変更しています。 Elixirでは変更はできません。 代わりに、関数型言語は再帰に依存しています。関数は、再帰アクションの継続を停止する条件に達するまで再帰的に呼び出されます。 このプロセスではデータは変更されません。

再帰を使用して、 n 回helloを出力する単純なループを記述しましょう。

defmodule Loop do
   def print_multiple_times(msg, n) when n <= 1 do
      IO.puts msg
   end

   def print_multiple_times(msg, n) do
      IO.puts msg
      print_multiple_times(msg, n - 1)
   end
end

Loop.print_multiple_times("Hello", 10)

上記のプログラムが実行されると、次の結果が生成されます-

Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello

関数のパターンマッチング手法と再帰を利用して、ループを正常に実装しました。 再帰的な定義を理解することは困難ですが、ループを再帰に変換することは簡単です。

Elixirは* Enumモジュール*を提供します。 このモジュールは、同じものの再帰的な定義を理解しようとするよりも簡単に使用できるため、最も反復的なループ呼び出しに使用されます。 これらについては次の章で説明します。 独自の再帰的な定義は、そのモジュールを使用したソリューションが見つからない場合にのみ使用してください。 これらの関数はテールコールに最適化されており、非常に高速です。

Elixir-列挙可能

列挙可能とは、列挙できるオブジェクトです。 「列挙」とは、セット/コレクション/カテゴリのメンバーを1つずつ(通常は順番に、通常は名前で)カウントオフすることを意味します。

Elixirは列挙型の概念と、列挙型と連携するためのhttps://elixir-lang.org/docs/stable/elixir/Enuml[Enum module]を提供します。 Enumモジュールの関数は、名前が示すように、データ構造内の値の列挙に制限されています。 列挙可能なデータ構造の例は、リスト、タプル、マップなどです。 Enumモジュールは、enumを処理する100を少し超える関数を提供します。 この章では、いくつかの重要な機能について説明します。

これらの関数はすべて、列挙可能な要素を最初の要素として、関数を2番目の要素として取り、それらを処理します。 以下に機能を説明します。

all?

*all* を使用する場合 関数では、コレクション全体がtrueに評価される必要があり、そうでない場合はfalseが返されます。 たとえば、リスト内のすべての要素が奇数であるかどうかを確認するには、次のようにします。
res = Enum.all?([1, 2, 3, 4], fn(s) -> rem(s,2) == 1 end)
IO.puts(res)

上記のプログラムが実行されると、次の結果が生成されます-

false

これは、このリストのすべての要素が奇数ではないためです。

any?

名前が示すように、この関数は、コレクションの要素がtrueと評価された場合にtrueを返します。 たとえば-

res = Enum.any?([1, 2, 3, 4], fn(s) -> rem(s,2) == 1 end)
IO.puts(res)

上記のプログラムが実行されると、次の結果が生成されます-

true

チャンク

この関数は、コレクションを2番目の引数として提供されるサイズの小さなチャンクに分割します。 たとえば-

res = Enum.chunk([1, 2, 3, 4, 5, 6], 2)
IO.puts(res)

上記のプログラムが実行されると、次の結果が生成されます-

[[each

It may be necessary to iterate over a collection without producing a new value, for this case we use the *each *function −

[source,prettyprint,notranslate]

Enum.each(["Hello"、 "Every"、 "one"]、fn(s)→ IO.puts(s)end)

When the above program is run, it produces the following result −

[source,result,notranslate]

みなさん、こんにちは

=== map

To apply our function to each item and produce a new collection we use the map function. It is one of the most useful constructs in functional programming as it is quite expressive and short. Let us consider an example to understand this. We will double the values stored in a list and store it in a new list* res *−

[source,prettyprint,notranslate]

res = Enum.map([2、5、3、6]、fn(a)→ a* 2 end)IO.puts(res)

When the above program is run, it produces the following result −

[source,result,notranslate]
=== reduce

The *reduce *function helps us reduce our enumerable to a single value. To do this, we supply an optional accumulator (5 in this example) to be passed into our function; if no accumulator is provided, the first value is used −

[source,prettyprint,notranslate]

res = Enum.reduce([1、2、3、4]、5、fn(x、accum)→ x + accum end)IO.puts(res)

When the above program is run, it produces the following result −

[source,result,notranslate]

15

The accumulator is the initial value passed to the* fn*. From the second call onwards the value returned from previous call is passed as accum. We can also use reduce without the accumulator −

[source,prettyprint,notranslate]

res = Enum.reduce([1、2、3、4]、fn(x、accum)→ x + accum end)IO.puts(res)

When the above program is run, it produces the following result −

[source,result,notranslate]

10

=== uniq

The uniq function removes duplicates from our collection and returns only the set of elements in the collection. For example −

[source,prettyprint,notranslate]

res = Enum.uniq([1、2、2、3、3、3、4、4、4、4])IO.puts(res)

When running above program, it produces the following result −

[source,result,notranslate]
=== Eager Evaluation

All the functions in the Enum module are eager. Many functions expect an enumerable and return a list back. This means that when performing multiple operations with Enum, each operation is going to generate an intermediate list until we reach the result. Let us consider the following example to understand this −

[source,prettyprint,notranslate]

odd? =&(奇数? =&(rem(&1、2)!= 0)res = 1..100_000 |> Enum.map(&(&1 *3))|> Enum.filter(odd?)|> Enum.sum IO.puts( res)

When the above program is run, it produces the following result −

[source,result,notranslate]

7500000000

The example above has a pipeline of operations. We start with a range and then multiply each element in the range by 3. This first operation will now create and return a list with 100_000 items. Then we keep all odd elements from the list, generating a new list, now with 50_000 items, and then we sum all entries.

The* |> *symbol used in the snippet above is the* pipe operator*: it simply takes the output from the expression on its left side and passes it as the first argument to the function call on its right side. It’s similar to the Unix | operator. Its purpose is to highlight the flow of data being transformed by a series of functions.

Without the *pipe *operator, the code looks complicated −

[source,prettyprint,notranslate]

Enum.sum(Enum.filter(Enum.map(1..100_000、&(&1* 3))、odd?))

We have many other functions, however, only a few important ones have been described here.

Elixir-ストリーム

多くの関数は列挙可能なものを期待し、 list を返します。 つまり、Enumを使用して複数の操作を実行している間、各操作は結果に到達するまで中間リストを生成します。

ストリームは、列挙型による積極的な操作ではなく、遅延操作をサポートします。 要するに、* streamsは、レイジーで構成可能な列挙可能要素です*。 つまり、絶対に必要な場合を除き、Streamsは操作を実行しません。 これを理解するための例を考えてみましょう-

odd? = &(rem(&1, 2) != 0)
res = 1..100_000 |> Stream.map(&(&1 * 3)) |> Stream.filter(odd?) |> Enum.sum
IO.puts(res)

上記のプログラムが実行されると、次の結果が生成されます-

7500000000

上記の例では、 1..100_000 |> Stream.map(&(&1 3))*は、範囲1..100_000でのマップ計算を表すデータ型、実際のストリームを返します。 この表現はまだ評価されていません。 ストリームは、中間リストを生成する代わりに、基になるストリームをEnumモジュールに渡すときにのみ呼び出される一連の計算を構築します。 ストリームは、大規模な、場合によっては無限のコレクションを操作するときに役立ちます。

ストリームと列挙型には多くの共通の機能があります。 ストリームは主に、入力列挙型の計算を実行した後に戻り値としてリストを生成したEnumモジュールが提供するのと同じ関数を提供します。 それらのいくつかは、次の表に記載されています-

Sr.No. Function and its Description
1

chunk(enum, n, step, leftover \\ nil)

それぞれがn個のアイテムを含むチャンクで列挙型をストリーミングします。各新しいチャンクは列挙型へのステップ要素を開始します。

2

concat(enumerables)

enumerable内の各enumerableを列挙するストリームを作成します。

3

each(enum, fun)

各アイテムに対して指定された関数を実行します。

4

filter(enum, fun)

列挙の指定された関数に従って要素をフィルタリングするストリームを作成します。

5

map(enum, fun)

指定された関数を列挙に適用するストリームを作成します。

6

drop(enum, n)

列挙可能から次のn個のアイテムを遅延ドロップします。

Elixir-構造体

構造体は、マップ上に構築された拡張機能であり、コンパイル時のチェックとデフォルト値を提供します。

構造の定義

構造体を定義するには、defstruct構造体が使用されます-

defmodule User do
   defstruct name: "John", age: 27
end

defstructで使用されるキーワードリストは、構造体がデフォルト値とともに持つフィールドを定義します。 構造体は、定義されているモジュールの名前を取ります。 上記の例では、Userという名前の構造体を定義しました。 これで、マップを作成するために使用される構文と同様の構文を使用して、ユーザー構造体を作成できます-

new_john = %User{})
ayush = %User{name: "Ayush", age: 20}
megan = %User{name: "Megan"})

上記のコードは、値を持つ3つの異なる構造体を生成します-

%User{age: 27, name: "John"}
%User{age: 20, name: "Ayush"}
%User{age: 27, name: "Megan"}

構造体は、defstructで定義されたフィールド(およびそれらすべて)のみが構造体に存在することを許可するコンパイル時の保証を提供します。 したがって、モジュールで構造体を作成すると、独自のフィールドを定義できません。

構造体へのアクセスと更新

マップについて説明したときに、マップのフィールドにアクセスして更新する方法を示しました。 同じ手法(および同じ構文)が構造体にも適用されます。 たとえば、前の例で作成したユーザーを更新する場合は、-

defmodule User do
   defstruct name: "John", age: 27
end
john = %User{}
#john right now is: %User{age: 27, name: "John"}

#To access name and age of John,
IO.puts(john.name)
IO.puts(john.age)

上記のプログラムが実行されると、次の結果が生成されます-

John
27

構造体の値を更新するには、マップの章で使用したのと同じ手順を再度使用します。

meg = %{john | name: "Meg"}

構造体は、特定のキーの値の一致と、一致する値が一致した値と同じ型の構造体であることを確認するために、パターンマッチングでも使用できます。

Elixir-プロトコル

プロトコルは、Elixirで多型を実現するメカニズムです。 プロトコルのディスパッチは、プロトコルを実装している限り、どのデータ型でも利用できます。

プロトコルの使用例を考えてみましょう。 前の章で to_string という関数を使用して、他の型から文字列型に変換しました。 これは実際にはプロトコルです。 エラーを生成せずに与えられた入力に従って動作します。 これは、パターンマッチング関数について説明しているように見えるかもしれませんが、さらに先に進むと、異なることがわかります。

プロトコルメカニズムをさらに理解するには、次の例を検討してください。

与えられた入力が空かどうかを表示するプロトコルを作成しましょう。 このプロトコルを*空白?*と呼びます。

プロトコルの定義

私たちは次の方法でElixirでプロトコルを定義できます-

defprotocol Blank do
   def blank?(data)
end

ご覧のとおり、関数の本体を定義する必要はありません。 他のプログラミング言語のインターフェースに精通している場合、プロトコルは本質的に同じものと考えることができます。

したがって、このプロトコルは、それを実装するものはすべて* empty?*関数を持っている必要があると言っていますが、関数の応答方法は実装者次第です。 プロトコルを定義したら、いくつかの実装を追加する方法を理解しましょう。

プロトコルの実装

プロトコルを定義したので、取得する可能性のあるさまざまな入力の処理方法を指定する必要があります。 先ほど取った例を基にしましょう。 リスト、マップ、および文字列に空のプロトコルを実装します。 これは、渡したものが空白かどうかを示します。

#Defining the protocol
defprotocol Blank do
   def blank?(data)
end

#Implementing the protocol for lists
defimpl Blank, for: List do
   def blank?([]), do: true
   def blank?(_), do: false
end

#Implementing the protocol for strings
defimpl Blank, for: BitString do
   def blank?(""), do: true
   def blank?(_), do: false
end

#Implementing the protocol for maps
defimpl Blank, for: Map do
   def blank?(map), do: map_size(map) == 0
end

IO.puts(Blank.blank? [])
IO.puts(Blank.blank? [:true, "Hello"])
IO.puts(Blank.blank? "")
IO.puts(Blank.blank? "Hi")

プロトコルを使用する上で理にかなっているものであれば、必要な数だけ、または少数のタイプに対してプロトコルを実装できます。 これはプロトコルの非常に基本的な使用例でした。 上記のプログラムが実行されると、次の結果が生成されます-

true
false
true
false

注意-プロトコルを定義したもの以外のタイプにこれを使用すると、エラーが発生します。

Elixir-ファイルIO

ファイルIOは、プログラミング言語がファイルシステム上のファイルと対話できるようにするため、あらゆるプログラミング言語の不可欠な部分です。 この章では、パスとファイルの2つのモジュールについて説明します。

パスモジュール

*path* モジュールは、ファイルシステム操作のヘルパーモジュールと見なすことができる非常に小さなモジュールです。 Fileモジュールの大部分の関数は、引数としてパスを必要とします。 最も一般的には、これらのパスは通常のバイナリになります。 Pathモジュールは、そのようなパスを操作するための機能を提供します。 Pathモジュールはさまざまなオペレーティングシステムを透過的に処理するため、単にバイナリを操作するのではなく、Pathモジュールの関数を使用することをお勧めします。 Elixirは、ファイル操作を実行するときにWindowsでスラッシュ(/)をバックスラッシュ(\)に自動的に変換することに注意してください。

Pathモジュールをさらに理解するために、次の例を考えてみましょう-

IO.puts(Path.join("foo", "bar"))

上記のプログラムが実行されると、次の結果が生成されます-

foo/bar

パスモジュールが提供する多くのメソッドがあります。 さまざまな方法については、http://elixir-lang.org/docs/stable/elixir/Pathl [こちら]をご覧ください。 これらのメソッドは、多くのファイル操作操作を実行している場合に頻繁に使用されます。

ファイルモジュール

ファイルモジュールには、IOデバイスとしてファイルを開くことができる関数が含まれています。 デフォルトでは、ファイルはバイナリモードで開かれるため、開発者はIOモジュールの特定の IO.binread および IO.binwrite 関数を使用する必要があります。 newfile というファイルを作成し、それにデータを書き込みましょう。

{:ok, file} = File.read("newfile", [:write])
# Pattern matching to store returned stream
IO.binwrite(file, "This will be written to the file")

私たちが書いたばかりのファイルを開くと、コンテンツは次のように表示されます-

This will be written to the file

ファイルモジュールの使用方法を理解しましょう。

ファイルを開く

ファイルを開くには、次の2つの機能のいずれかを使用できます-

{:ok, file} = File.open("newfile")
file = File.open!("newfile")
*File.open* 関数と* File.open!()*関数の違いを理解しましょう。
  • File.open 関数は常にタプルを返します。 ファイルが正常に開かれた場合、タプルの最初の値を*:ok ​​として返し、2番目の値はio_device型のリテラルです。 エラーが発生した場合、最初の値が:error *で2番目の値が理由としてタプルを返します。
  • 一方、* File.open!()関数は、ファイルが正常に開かれた場合は *io_device を返し、それ以外の場合はエラーを発生させます。 注:これは、これから説明するすべてのファイルモジュール関数で使用されるパターンです。

このファイルを開くモードを指定することもできます。 ファイルを読み取り専用で、utf-8エンコーディングモードで開くには、次のコードを使用します-

file = File.open!("newfile", [:read, :utf8])

ファイルへの書き込み

ファイルに書き込む方法は2つあります。 Fileモジュールのwrite関数を使用した最初のものを見てみましょう。

File.write("newfile", "Hello")

ただし、同じファイルに複数の書き込みを行う場合は、これを使用しないでください。 この関数が呼び出されるたびに、ファイル記述子が開かれ、ファイルに書き込むための新しいプロセスが生成されます。 ループで複数の書き込みを行う場合は、 File.open を介してファイルを開き、IOモジュールのメソッドを使用して書き込みます。 同じことを理解するための例を考えてみましょう-

#Open the file in read, write and utf8 modes.
file = File.open!("newfile_2", [:read, :utf8, :write])

#Write to this "io_device" using standard IO functions
IO.puts(file, "Random text")
*IO.write* や *IO.binwrite* などの他のIOモジュールメソッドを使用して、io_deviceとして開かれたファイルに書き込むことができます。

ファイルからの読み取り

ファイルから読み取る方法は2つあります。 Fileモジュールのread関数を使用して、最初のものを見てみましょう。

IO.puts(File.read("newfile"))

このコードを実行すると、最初の要素が*:ok *​​で、2番目の要素がnewfileの内容であるタプルを取得する必要があります

  • File.read!*関数を使用して、返されたファイルの内容を取得することもできます。

開いているファイルを閉じる

File.open関数を使用してファイルを開くときは、使用後、 File.close 関数を使用してファイルを閉じる必要があります-

File.close(file)

Elixir-プロセス

Elixirでは、すべてのコードはプロセス内で実行されます。 プロセスは互いに分離され、互いに並行して実行され、メッセージパッシングを介して通信します。 Elixirのプロセスをオペレーティングシステムのプロセスと混同しないでください。 Elixirのプロセスは、メモリとCPUの点で非常に軽量です(他の多くのプログラミング言語のスレッドとは異なります)。 このため、数万または数十万ものプロセスが同時に実行されることも珍しくありません。

この章では、新しいプロセスを生成し、異なるプロセス間でメッセージを送受信するための基本的な構成について学習します。

スポーン関数

新しいプロセスを作成する最も簡単な方法は、 spawn 関数を使用することです。 spawn は、新しいプロセスで実行される関数を受け入れます。 たとえば-

pid = spawn(fn -> 2 * 2 end)
Process.alive?(pid)

上記のプログラムが実行されると、次の結果が生成されます-

false

spawn関数の戻り値はPIDです。 これはプロセスの一意の識別子であるため、PIDの上でコードを実行すると、異なるコードになります。 この例でわかるように、プロセスが生きているかどうかを確認すると、プロセスは停止しています。 これは、指定された関数の実行が終了するとすぐにプロセスが終了するためです。

既に述べたように、すべてのElixirコードはプロセス内で実行されます。 自己機能を実行すると、現在のセッションのPIDが表示されます-

pid = self

Process.alive?(pid)

上記のプログラムを実行すると、次の結果が生成されます-

true

メッセージの受け渡し

メッセージを send でプロセスに送信し、 receive で受信できます。 現在のプロセスにメッセージを渡し、同じプロセスで受信してみましょう。

send(self(), {:hello, "Hi people"})

receive do
   {:hello, msg} -> IO.puts(msg)
   {:another_case, msg} -> IO.puts("This one won't match!")
end

上記のプログラムが実行されると、次の結果が生成されます-

Hi people

send関数を使用して現在のプロセスにメッセージを送信し、selfのPIDに渡しました。 次に、 receive 関数を使用して着信メッセージを処理しました。

メッセージがプロセスに送信されると、メッセージは*プロセスメールボックス*に保存されます。 受信ブロックは、現在のプロセスメールボックスを通過して、指定されたパターンのいずれかに一致するメッセージを検索します。 受信ブロックは、ガードや、caseなどの多くの句をサポートしています。

パターンのいずれかに一致するメールボックスにメッセージがない場合、現在のプロセスは一致するメッセージが到着するまで待機します。 タイムアウトも指定できます。 例えば、

receive do
   {:hello, msg}  -> msg
after
   1_000 -> "nothing after 1s"
end

上記のプログラムが実行されると、次の結果が生成されます-

nothing after 1s

-メッセージがすでにメールボックスにあると予想される場合、0のタイムアウトを指定できます。

リンク集

Elixirでの最も一般的なスポーン形式は、実際には spawn_link 関数によるものです。 spawn_linkの例を見る前に、プロセスが失敗したときに何が起こるかを理解しましょう。

spawn fn -> raise "oops" end

上記のプログラムを実行すると、次のエラーが生成されます-

[error] Process #PID<0.58.00> raised an exception
** (RuntimeError) oops
   :erlang.apply/2

エラーをログに記録しましたが、生成プロセスはまだ実行中です。 これは、プロセスが分離されているためです。 あるプロセスの失敗を別のプロセスに伝播させるには、それらをリンクする必要があります。 これは、 spawn_link 関数を使用して実行できます。 同じことを理解するための例を考えてみましょう-

spawn_link fn -> raise "oops" end

上記のプログラムを実行すると、次のエラーが生成されます-

* *(EXIT from #PID<0.41.0>) an exception was raised:
  * * (RuntimeError) oops
      :erlang.apply/2

これを iex シェルで実行している場合、シェルはこのエラーを処理し、終了しません。 ただし、最初にスクリプトファイルを作成してから elixir <file-name> .exs を使用して実行した場合、このエラーにより親プロセスも停止します。

フォールトトレラントシステムを構築するとき、プロセスとリンクは重要な役割を果たします。 Elixirアプリケーションでは、しばしばプロセスをスーパーバイザーにリンクします。スーパーバイザーは、プロセスが終了したときを検出し、その場所で新しいプロセスを開始します。 これは、プロセスが分離されており、デフォルトでは何も共有しないためにのみ可能です。 プロセスは分離されているため、プロセスの障害がクラッシュしたり、別のプロセスの状態を破壊したりすることはありません。 他の言語では、例外をキャッチ/処理する必要があります。 Elixirでは、スーパーバイザーがシステムを適切に再起動することを期待しているため、実際にプロセスを失敗させても問題ありません。

状態

たとえば、アプリケーション構成を保持するために状態を必要とするアプリケーションを構築する場合、またはファイルを解析してメモリに保持する必要がある場合、どこに保存しますか? Elixirのプロセス機能は、このようなことを行うときに便利です。

無限にループし、状態を維持し、メッセージを送受信するプロセスを作成できます。 例として、 kv.exs という名前のファイルにキーと値のストアとして機能する新しいプロセスを開始するモジュールを作成してみましょう。

defmodule KV do
   def start_link do
      Task.start_link(fn -> loop(%{}) end)
   end

   defp loop(map) do
      receive do
         {:get, key, caller} ->
         send caller, Map.get(map, key)
         loop(map)
         {:put, key, value} ->
         loop(Map.put(map, key, value))
      end
   end
end
*start_link* 関数は、空のマップで始まる *loop* 関数を実行する新しいプロセスを開始することに注意してください。 次に、 *loop* 関数はメッセージを待機し、各メッセージに対して適切なアクションを実行します。 *:get *メッセージの場合、メッセージを発信者に送り、ループを再度呼び出して新しいメッセージを待ちます。 *:put *メッセージは、指定されたキーと値が保存されたマップの新しいバージョンで実際に *loop* を呼び出します。

私たちは今、次を実行しましょう-

iex kv.exs

これで、 iex シェルになっているはずです。 私たちのモジュールをテストするには、次を試してください-

{:ok, pid} = KV.start_link

# pid now has the pid of our new process that is being
# used to get and store key value pairs

# Send a KV pair :hello, "Hello" to the process
send pid, {:put, :hello, "Hello"}

# Ask for the key :hello
send pid, {:get, :hello, self()}

# Print all the received messages on the current process.
flush()

上記のプログラムが実行されると、次の結果が生成されます-

"Hello"

エリクサー-シギル

この章では、シギル、つまりテキスト表現を操作するために言語が提供するメカニズムについて説明します。 印章は、チルダ(〜)文字で始まり、その後に文字(印章を識別する)と区切り文字が続きます。オプションで、最後の区切り文字の後に修飾子を追加できます。

正規表現

Elixirの正規表現はシギルです。 Stringの章でそれらの使用を見てきました。 もう一度例を挙げて、Elixirで正規表現を使用する方法を見てみましょう。

# A regular expression that matches strings which contain "foo" or
# "bar":
regex = ~r/foo|bar/
IO.puts("foo" =~ regex)
IO.puts("baz" =~ regex)

上記のプログラムが実行されると、次の結果が生成されます-

true
false

Sigilsは8つの異なる区切り文字をサポートしています-

~r/hello/
~r|hello|
~r"hello"
~r'hello'
~r(hello)
~r[hello]
~r{hello}
~r<hello>

異なる区切り文字をサポートする理由は、異なる区切り文字が異なるシギルにより適している可能性があるためです。 たとえば、正規表現に括弧を使用すると、正規表現内の括弧と混同される可能性があるため、混乱を招く選択になる場合があります。 ただし、次のセクションで説明するように、括弧は他のシギルに便利です。

ElixirはPerl互換の正規表現をサポートし、修飾子もサポートします。 正規表現の使用の詳細については、https://www.finddevguides.com/perl/perl_regular_expressions [こちら]を参照してください。

文字列、文字リスト、および単語リスト

正規表現以外に、Elixirには3つの組み込みシギルがあります。 シギルを見てみましょう。

文字列

〜s sigilは、二重引用符のように文字列を生成するために使用されます。 〜s sigilは、たとえば、文字列に二重引用符と単一引用符の両方が含まれる場合に便利です-

new_string = ~s(this is a string with "double" quotes, not 'single' ones)
IO.puts(new_string)

このシギルは文字列を生成します。 上記のプログラムが実行されると、次の結果が生成されます-

"this is a string with \"double\" quotes, not 'single' ones"

文字リスト

〜c sigilは、文字リストを生成するために使用されます-

new_char_list = ~c(this is a char list containing 'single quotes')
IO.puts(new_char_list)

上記のプログラムが実行されると、次の結果が生成されます-

this is a char list containing 'single quotes'

単語リスト

〜w印は、単語のリストを生成するために使用されます(単語は単なる通常の文字列です)。 〜w印の内部では、単語は空白で区切られています。

new_word_list = ~w(foo bar bat)
IO.puts(new_word_list)

上記のプログラムが実行されると、次の結果が生成されます-

foobarbat

〜w sigilは、結果リストの要素のデータ型を指定する c、s 、および a 修飾子(それぞれ文字リスト、文字列、アトム用)も受け入れます-

new_atom_list = ~w(foo bar bat)a
IO.puts(new_atom_list)

上記のプログラムが実行されると、次の結果が生成されます-

[:foo, :bar, :bat]

Sigilsでの補間とエスケープ

小文字のシギルに加えて、Elixirは大文字のシギルをサポートして、文字のエスケープと補間を処理します。 〜sと〜Sは両方とも文字列を返しますが、前者はエスケープコードと補間を許可しますが、後者は許可しません。 これを理解するための例を考えてみましょう-

~s(String with escape codes \x26 #{"inter" <> "polation"})
# "String with escape codes & interpolation"
~S(String without escape codes \x26 without #{interpolation})
# "String without escape codes \\x26 without \#{interpolation}"

カスタム印章

独自のカスタムシギルを簡単に作成できます。 この例では、文字列を大文字に変換するシギルを作成します。

defmodule CustomSigil do
   def sigil_u(string, []), do: String.upcase(string)
end

import CustomSigil

IO.puts(~u/tutorials point/)

上記のコードを実行すると、次の結果が生成されます-

TUTORIALS POINT

最初にCustomSigilというモジュールを定義し、そのモジュール内でsigil_uという関数を作成しました。 既存のシギル空間には既存の〜uシギルがないため、これを使用します。 _uは、チルダの後の文字としてuを使用することを示します。 関数定義は、入力とリストの2つの引数をとる必要があります。

エリクサー-理解

リストの内包表記は、Elixirで列挙可能なものをループするための構文糖衣です。 この章では、反復と生成に内包表記を使用します。

基本

enumerablesの章でEnumモジュールを見ると、map関数に出会いました。

Enum.map(1..3, &(&1 *2))

この例では、関数を2番目の引数として渡します。 範囲内の各アイテムは関数に渡され、新しい値を含む新しいリストが返されます。

マッピング、フィルタリング、および変換はElixirで非常に一般的なアクションであるため、前の例と同じ結果を達成するわずかに異なる方法があります-

for n <- 1..3, do: n* 2

上記のコードを実行すると、次の結果が生成されます-

[2, 4, 6]

2番目の例は理解度であり、おそらくおわかりのように、 Enum.map 関数を使用した場合に達成できることの単なる構文上の砂糖です。 ただし、パフォーマンスの点で、Enumモジュールの関数よりも内包表記を使用することには実際の利点はありません。

内包表記はリストに限定されず、すべての列挙可能オブジェクトで使用できます。

フィルタ

フィルターは内包表記の一種のガードと考えることができます。 フィルターされた値が false または nil を返す場合、最終リストから除外されます。 範囲をループして、偶数のみを心配しましょう。 整数モジュールの is_even 関数を使用して、値が偶数かどうかを確認します。

import Integer
IO.puts(for x <- 1..10, is_even(x), do: x)

上記のコードを実行すると、次の結果が生成されます-

[2, 4, 6, 8, 10]

同じ理解で複数のフィルターを使用することもできます。 コンマで区切られた is_even フィルターの後に、必要な別のフィルターを追加します。

:intoオプション

上記の例では、すべての内包表記が結果としてリストを返しました。 ただし、内包表記の結果は、*:into *オプションを内包表記に渡すことにより、異なるデータ構造に挿入できます。

たとえば、文字列内のすべてのスペースを簡単に削除するために、:intoオプションで bitstring ジェネレータを使用できます-

IO.puts(for <<c <- " hello world ">>, c != ?\s, into: "", do: <<c>>)

上記のコードを実行すると、次の結果が生成されます-

helloworld

上記のコードは、 c!=?\ s フィルターを使用して文字列からすべてのスペースを削除し、:intoオプションを使用して、返されたすべての文字を文字列に入れます。

Elixir-タイプスペック

Elixirは動的に型指定された言語であるため、Elixirのすべての型はランタイムによって推測されます。 それにもかかわらず、Elixirにはtypespecが付属しています。これは、*カスタムデータ型の宣言と型付き関数のシグネチャ(仕様)*の宣言に使用される表記法です。

機能仕様(仕様)

デフォルトでは、Elixirは整数やpidなどのいくつかの基本型、および複雑な型も提供します。たとえば、 round 関数は、floatを最も近い整数に丸め、数値を引数(整数またはfloat )整数を返します。 関連するhttps://elixir-lang.org/docs/stable/elixir/Kernell#round/1[documentation]では、丸められた署名は次のように書かれています-

round(number) :: integer

上記の説明は、左側の関数が括弧で指定されたものを引数として受け取り、::の右側にあるもの、つまり整数を返すことを意味します。 関数仕様は、関数定義の直前に置かれた @ spec ディレクティブで記述されます。 ラウンド関数は次のように書くことができます-

@spec round(number) :: integer
def round(number), do: # Function implementation
...

Typespecは複雑な型もサポートしています。たとえば、整数のリストを返す場合は、 [Integer] を使用できます

カスタムタイプ

Elixirは多くの便利な組み込み型を提供しますが、必要に応じてカスタム型を定義すると便利です。 これは、@ typeディレクティブを使用してモジュールを定義するときに実行できます。 同じことを理解するための例を考えてみましょう-

defmodule FunnyCalculator do
   @type number_with_joke :: {number, String.t}

   @spec add(number, number) :: number_with_joke
   def add(x, y), do: {x + y, "You need a calculator to do that?"}

   @spec multiply(number, number) :: number_with_joke
   def multiply(x, y), do: {x * y, "It is like addition on steroids."}
end

{result, comment} = FunnyCalculator.add(10, 20)
IO.puts(result)
IO.puts(comment)

上記のプログラムが実行されると、次の結果が生成されます-

30
You need a calculator to do that?

-@typeを介して定義されたカスタムタイプはエクスポートされ、定義されているモジュールの外部で利用可能です。 カスタムタイプをプライベートに保ちたい場合は、 @ type の代わりに @ typep ディレクティブを使用できます。

エリクサー-行動

Elixir(およびErlang)の動作は、コンポーネントの一般的な部分(動作モジュールになる)と特定の部分(コールバックモジュールになる)を分離して抽象化する方法です。 行動はする方法を提供します-

  • モジュールによって実装される必要がある一連の関数を定義します。
  • モジュールがそのセットのすべての機能を実装していることを確認してください。

必要に応じて、Javaのようなオブジェクト指向言語のインターフェースのような振る舞いを考えることができます:モジュールが実装しなければならない関数シグネチャのセット。

動作の定義

独自の動作を作成し、この一般的な動作を使用してモジュールを作成する例を考えてみましょう。 私たちは、異なる言語で人々に挨拶と挨拶をする行動を定義します。

defmodule GreetBehaviour do
   @callback say_hello(name :: string) :: nil
   @callback say_bye(name :: string) :: nil
end
*@ callback* ディレクティブは、採用モジュールが定義する必要がある関数をリストするために使用されます。 また、noも指定します。 引数の種類、その戻り値。

行動を採用する

動作を正常に定義しました。 次に、複数のモジュールでそれを採用および実装します。 この動作を英語とスペイン語で実装する2つのモジュールを作成しましょう。

defmodule GreetBehaviour do
   @callback say_hello(name :: string) :: nil
   @callback say_bye(name :: string) :: nil
end

defmodule EnglishGreet do
   @behaviour GreetBehaviour
   def say_hello(name), do: IO.puts("Hello " <> name)
   def say_bye(name), do: IO.puts("Goodbye, " <> name)
end

defmodule SpanishGreet do
   @behaviour GreetBehaviour
   def say_hello(name), do: IO.puts("Hola " <> name)
   def say_bye(name), do: IO.puts("Adios " <> name)
end

EnglishGreet.say_hello("Ayush")
EnglishGreet.say_bye("Ayush")
SpanishGreet.say_hello("Ayush")
SpanishGreet.say_bye("Ayush")

上記のプログラムが実行されると、次の結果が生成されます-

Hello Ayush
Goodbye, Ayush
Hola Ayush
Adios Ayush

すでに見たように、モジュールで @ behaviour ディレクティブを使用した動作を採用しています。 すべての_child_モジュールの動作に実装されているすべての関数を定義する必要があります。 これは、OOP言語のインターフェイスとほぼ同等と見なすことができます。

Elixir-エラー処理

Elixirには、エラー、スロー、終了の3つのエラーメカニズムがあります。 各メカニズムを詳細に調べてみましょう。

エラー

エラー(または例外)は、コードで例外的なことが発生したときに使用されます。 サンプルエラーは、文字列に数字を追加しようとすることで取得できます-

IO.puts(1 + "Hello")

上記のプログラムを実行すると、次のエラーが生成されます-

** (ArithmeticError) bad argument in arithmetic expression
   :erlang.+(1, "Hello")

これはサンプルの組み込みエラーでした。

エラーを発生させる

raise関数を使用してエラーを raise できます。 同じことを理解するための例を考えてみましょう-

#Runtime Error with just a message
raise "oops"  # * *(RuntimeError) oops

他のエラーはraise/2でエラー名とキーワード引数のリストを渡すことで発生する可能性があります

#Other error type with a message
raise ArgumentError, message: "invalid argument foo"

また、独自のエラーを定義して発生させることもできます。 次の例を考慮してください-

defmodule MyError do
   defexception message: "default message"
end

raise MyError  # Raises error with default message
raise MyError, message: "custom message"  # Raises error with custom message

エラーの救済

プログラムを突然終了させたくないのですが、エラーを注意深く処理する必要があります。 このために、エラー処理を使用します。* try/rescue *コンストラクトを使用して、エラーを*レスキュー*します。 同じことを理解するために、次の例を考えてみましょう-

err = try do
   raise "oops"
rescue
   e in RuntimeError -> e
end

IO.puts(err.message)

上記のプログラムが実行されると、次の結果が生成されます-

oops

パターンマッチングを使用して、rescueステートメントのエラーを処理しました。 エラーを使用せず、単に識別目的で使用する場合は、フォームを使用することもできます-

err = try do
   1 + "Hello"
rescue
   RuntimeError -> "You've got a runtime error!"
   ArithmeticError -> "You've got a Argument error!"
end

IO.puts(err)

上記のプログラムを実行すると、次の結果が生成されます-

You've got a Argument error!

-Elixir標準ライブラリのほとんどの関数は2回実装されます。1回はタプルを返し、もう1回はエラーを発生させます。 たとえば、 File.read および* File.read!*関数。 最初のファイルは、ファイルが正常に読み取られた場合にタプルを返し、エラーが発生した場合は、このタプルを使用してエラーの理由を示しました。 エラーが発生した場合は、2番目のエラーが発生しました。

最初の関数アプローチを使用する場合、エラーに一致するパターンのケースを使用し、それに応じてアクションを実行する必要があります。 2番目のケースでは、エラーを起こしやすいコードに対してtry rescueアプローチを使用し、それに応じてエラーを処理します。

投げる

Elixirでは、値をスローして後でキャッチできます。 スローとキャッチは、スローとキャッチを使用しない限り値を取得できない状況のために予約されています。

インスタンスは、ライブラリとインターフェイスする場合を除いて、実際には非常にまれです。 たとえば、Enumモジュールが値を見つけるためのAPIを提供していないこと、および数値のリストで13の最初の倍数を見つける必要があると仮定します。

val = try do
   Enum.each 20..100, fn(x) ->
      if rem(x, 13) == 0, do: throw(x)
   end
   "Got nothing"
catch
   x -> "Got #{x}"
end

IO.puts(val)

上記のプログラムが実行されると、次の結果が生成されます-

Got 26

Exit

プロセスが「自然な原因」(たとえば、未処理の例外)で終了すると、終了シグナルを送信します。 プロセスは、終了シグナルを明示的に送信することによって停止することもできます。 私たちは次の例を考えてみましょう-

spawn_link fn -> exit(1) end

上記の例では、値1の終了シグナルを送信することにより、リンクされたプロセスが停止しました。 try/catchを使用してexitを「キャッチ」することもできます。 たとえば-

val = try do
   exit "I am exiting"
catch
   :exit, _ -> "not really"
end

IO.puts(val)

上記のプログラムが実行されると、次の結果が生成されます-

not really

後に

場合によっては、エラーを発生させる可能性のある何らかのアクションの後にリソースをクリーンアップする必要があります。 try/afterコンストラクトを使用すると、これを実行できます。 たとえば、何か問題が発生した場合でも、ファイルを開き、after句を使用してファイルを閉じることができます。

{:ok, file} = File.open "sample", [:utf8, :write]
try do
   IO.write file, "olá"
   raise "oops, something went wrong"
after
   File.close(file)
end

このプログラムを実行すると、エラーが発生します。 ただし、 after ステートメントは、そのようなイベントが発生したときにファイル記述子が閉じられるようにします。

エリクサー-マクロ

マクロは、Elixirの最も高度で強力な機能の1つです。 すべての言語のすべての高度な機能と同様に、マクロは控えめに使用する必要があります。 コンパイル時に強力なコード変換を実行できます。 これから、マクロとは何か、マクロを簡単に使用する方法を理解します。

見積もり

マクロについて説明する前に、まずElixirの内部構造を見てみましょう。 Elixirプログラムは、独自のデータ構造で表すことができます。 Elixirプログラムの構成要素は、3つの要素を持つタプルです。 たとえば、関数呼び出しsum(1、2、3)は、内部的に次のように表されます-

{:sum, [], [1, 2, 3]}

最初の要素は関数名、2番目はメタデータを含むキーワードリスト、3番目は引数リストです。 次のように記述すれば、iexシェルの出力としてこれを取得できます-

quote do: sum(1, 2, 3)

演算子もそのようなタプルとして表されます。 最後の要素がリストではなくアトムであることを除いて、変数もそのようなトリプレットを使用して表されます。 より複雑な式を引用すると、コードがそのようなタプルで表されていることがわかります。このタプルは、ツリーに似た構造で互いに入れ子になっていることがよくあります。 多くの言語は、そのような表現を* Abstract Syntax Tree(AST)*と呼びます。 Elixirはこれらの引用表現を呼び出します。

引用を解除

コードの内部構造を取得できるようになったので、どのように変更しますか? 新しいコードまたは値を挿入するには、 unquote を使用します。 式の引用を解除すると、評価されてASTに挿入されます。 概念を理解するための例(iexシェル)を考えてみましょう-

num = 25

quote do: sum(15, num)

quote do: sum(15, unquote(num))

上記のプログラムが実行されると、次の結果が生成されます-

{:sum, [], [15, {:num, [], Elixir}]}
{:sum, [], [15, 25]}

引用式の例では、numを25に自動的に置き換えませんでした。 ASTを変更する場合は、この変数の引用符を外す必要があります。

マクロ

引用と非引用に精通したので、マクロを使用してElixirでメタプログラミングを探索できます。

最も簡単に言えば、マクロは、アプリケーションコードに挿入される引用符付きの式を返すように設計された特別な関数です。 マクロが関数のように呼び出されるのではなく、引用符で囲まれた式に置き換えられることを想像してください。 マクロを使用すると、Elixirを拡張し、アプリケーションにコードを動的に追加するために必要なものがすべて揃います

マクロとしての場合を除き、実装しましょう。 まず、 defmacro マクロを使用してマクロを定義します。 マクロは引用符で囲まれた式を返す必要があることに注意してください。

defmodule OurMacro do
   defmacro unless(expr, do: block) do
      quote do
         if !unquote(expr), do: unquote(block)
      end
   end
end

require OurMacro

OurMacro.unless true, do: IO.puts "True Expression"

OurMacro.unless false, do: IO.puts "False expression"

上記のプログラムが実行されると、次の結果が生成されます-

False expression

ここで起こっているのは、コードが_unless_マクロによって返される引用符で囲まれたコードに置き換えられていることです。 式を引用符で囲まずに現在のコンテキストで評価し、doブロックも引用符で囲まずにコンテキストで実行します。 この例は、エリクサーのマクロを使用したメタプログラミングを示しています。

マクロは、はるかに複雑なタスクで使用できますが、控えめに使用する必要があります。 これは、メタプログラミングは一般に悪い習慣と見なされており、必要な場合にのみ使用する必要があるためです。

Elixir-ライブラリ

Elixirは、Erlangライブラリとの優れた相互運用性を提供します。 いくつかのライブラリについて簡単に説明します。

バイナリモジュール

組み込みのElixir Stringモジュールは、UTF-8でエンコードされたバイナリを処理します。 バイナリモジュールは、UTF-8でエンコードされているとは限らないバイナリデータを処理する場合に役立ちます。 バイナリモジュールをさらに理解するための例を考えてみましょう-

# UTF-8
IO.puts(String.to_char_list("Ø"))

# binary
IO.puts(:binary.bin_to_list "Ø")

上記のプログラムが実行されると、次の結果が生成されます-

[216]
[195, 152]

上記の例は違いを示しています。 StringモジュールはUTF-8コードポイントを返し、:binaryは生データバイトを処理します。

暗号モジュール

暗号化モジュールには、ハッシュ関数、デジタル署名、暗号化などが含まれています。 このモジュールはErlang標準ライブラリの一部ではありませんが、Erlangディストリビューションに含まれています。 つまり、使用するときは常にプロジェクトのアプリケーションリストに:cryptoをリストする必要があります。 暗号モジュールを使用した例を見てみましょう-

IO.puts(Base.encode16(:crypto.hash(:sha256, "Elixir")))

上記のプログラムが実行されると、次の結果が生成されます-

3315715A7A3AD57428298676C5AE465DADA38D951BDFAC9348A8A31E9C7401CB

Digraphモジュール

digraphモジュールには、頂点とエッジで構築された有向グラフを処理するための関数が含まれています。 グラフを作成した後、そこにあるアルゴリズムは、たとえば、2つの頂点間の最短経路、またはグラフ内のループを見つけるのに役立ちます。 関数 in:digraph は、追加された頂点またはエッジを返す一方で、副作用として間接的にグラフ構造を変更することに注意してください。

digraph = :digraph.new()
coords = [{0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}]
[v0, v1, v2] = (for c <- coords, do: :digraph.add_vertex(digraph, c))
:digraph.add_edge(digraph, v0, v1)
:digraph.add_edge(digraph, v1, v2)
for point <- :digraph.get_short_path(digraph, v0, v2) do
   {x, y} = point
   IO.puts("#{x}, #{y}")
end

上記のプログラムが実行されると、次の結果が生成されます-

0.0, 0.0
1.0, 0.0
1.0, 1.0

数学モジュール

数学モジュールには、三角法、指数関数、対数関数をカバーする一般的な数学演算が含まれています。 Mathモジュールの動作を理解するために、次の例を考えてみましょう-

# Value of pi
IO.puts(:math.pi())

# Logarithm
IO.puts(:math.log(7.694785265142018e23))

# Exponentiation
IO.puts(:math.exp(55.0))

#...

上記のプログラムが実行されると、次の結果が生成されます-

3.141592653589793
55.0
7.694785265142018e23

キューモジュール

キューは、(ダブルエンド)FIFO(先入れ先出し)キューを効率的に実装するデータ構造です。 次の例は、キューモジュールの仕組みを示しています-

q = :queue.new
q = :queue.in("A", q)
q = :queue.in("B", q)
{{:value, val}, q} = :queue.out(q)
IO.puts(val)
{{:value, val}, q} = :queue.out(q)
IO.puts(val)

上記のプログラムが実行されると、次の結果が生成されます-

A
B