プログラミングElixir第10章~コレクションの処理

概要

  • Enumモジュール
  • Streamモジュール
  • Collectableプロトコル
  • 内包表記 (comprehension)

前章までに リスト と マップ というコレクションとして振る舞う型を見てきた。他にもいくつもあるらしい。

共通的なコトは、コレクションが持っている要素?に対して、繰り返し処理ができるということ。 `Enumerableプロトコルを実装しているといえる` という表現をしている。

Streamモジュールは.. 少し先に見たことがある、パイプ演算子を挟んで処理の並列化で前段の終了を待たずにデータを流し込む、遅延処理してくれるようなもの...かな.

Enum - コレクションの処理

詳しくは Elixir schoolのEnumの章を読むとして、おおよそ以下の機能をもつ:

  • コレクションをリストに変換
  • コレクションを結合する
  • 与えられたコレクションの各要素に、何らかの関数を適用した結果を要素とする新しいコレクションを生成する
  • 位置、基準によって要素を選択(フィルタリング)
  • 要素をソート、比較する
  • コレクションを分割する
  • 要素を連結する
  • 術後演算
  • コレクションをマージする

・・・・

やってみよう

Enum.all を 作る

期待値

iex(5)> Enum.all?( [2,4,6], &(rem(&1,2)==0) )
true

試行錯誤の結果

defmodule MyEnum do
  defp _all([_], false, _), do: false
  defp _all([_], nil, _), do: false
  defp _all([], _, _), do: true
  defp _all([h|t], _, f), do: _all(t, f.(h), f)

  def all?([h|t], f), do: (
    _all(t, f.(h), f)
  )
end

おけ

iex(7)> MyEnum.all?( [2,4,6], &(rem(&1,2)==0) )
true

Enum.eachをつくる

期待値

iex(9)> Enum.each(["some", "example"], fn x -> IO.puts(x) end)
some
example
:ok
defmodule MyEnum do
  defp _each([h], func), do: func.(h)
  defp _each([h|t], func), do: (
    func.(h)
    _each(t,func)
  )

  def each([h|t], func), do: (
    _each([h|t], func)
  )
end
iex(18)> MyEnum.each(["some", "example"], fn x -> IO.puts(x) end)
some
example
:ok

Enum.filter を 作る

defmodule MyEnum do
  defp _filter([], _func, result), do: result
  defp _filter([h|t], func, result), do: (
    if func.(h) do
      _filter(t, func, result++[h])
    else
      _filter(t, func, result)
    end
  )

  def filter([h|t], func), do: (
    _filter([h|t], func, [])
  )
end
iex(31)> MyEnum.filter([1, 2, 3,4,5,6,7], fn x -> rem(x, 2) == 0 end)
[2, 4, 6]

不具合

誤: _filter(t, func, [result|h])
正: _filter(t, func, result++[h])

resultがリストであることを期待して記述したつもり。縦棒は要素を区切るものだったから 誤りではリストと新しいスカラ値の2要素のリストが生成された。

リストに要素を追加する場合は `++` 演算子を使う。

MyEnum.filter([1, 2, 3], fn x -> rem(x, 2) == 0 end)
iex(22)> MyEnum.filter([1, 2, 3], fn x -> rem(x, 2) == 0 end)
[[] | 2]

Enum.split を 作る

TODO: あとで作る? リスト、番号を引数に持つ。

ローカル関数で結果のタプルを引数に渡して呼び出す。要素数を自然数で受けるようにして、前からいくつめかを認識させ、1ずつ減らしてヘッドから要素を取り出していく。結果のタプルを都度更新していけば結果が得られるだろう。

Enum.take を 作る

TODO: あとで作る?

splitと似たような実装。結果は要素ひとつにすれば同じように作れる?(共通化は?)

ストリーム~遅延列挙

Enumはデータすべてを処理しきる必要があり、パイプ演算子で結合した段それぞれで処理の完了を待つ。ストリーム対応であれば、処理の完了を待たずに逐次出力されたデータを次の段に渡していく。最終的にListに入れる場合などは、すべてのデータが入力されるのを待つ処理が入る。

・・・・そんな感じ?

プログラミングElixir第8章~コレクション

マップとキーワードリストの使いわけ

  1. パターンマッチを行いたいか→ マップを使う
  2. キーが複数エントリ存在するか→ Keywordモジュール
  3. 要素の順番を保証しなければならないか→ Keywordモジュール
  4. そのほか→ マップを使う

キーワードリスト

defmodule Canvas do
  @defaults [ fg: "black", bg: "white", font: "Arial" ]

  def draw_text( text, options \\ [] ) do
    options = Keyword.merge(@defaults, options)
    IO.puts "Drawing test #{inspect(text)}"
    IO.puts "Foreground:  #{options[:fg]}"
    IO.puts "Background:  #{Keyword.get(options, :bg)}"
  end
end
iex(3)> Canvas.draw_text( "test", fg: "red" )
Drawing test "test"
Foreground:  red
Background:  white
:ok

iex(5)> Canvas.draw_text( "test", [])
Drawing test "test"
Foreground:  black
Background:  white
:ok

iex(6)> Canvas.draw_text( "test")
Drawing test "test"
Foreground:  black
Background:  white
:ok

マップ

キーと値のセットというデータ構造を扱うもの。制限が緩いので多用途?

iex(1)> defaults = %{ fg: "black", bg: "white", font: "Arial" }
%{bg: "white", fg: "black", font: "Arial"}

iex(2)> Map.keys defaults
[:bg, :fg, :font]

iex(3)> Map.values defaults
["white", "black", "Arial"]

iex(6)> Map.drop defaults , [:font, :bg]
%{fg: "black"}
iex(7)> Map.put defaults, :sound, "none"
%{bg: "white", fg: "black", font: "Arial", sound: "none"}
iex(8)> defaults
%{bg: "white", fg: "black", font: "Arial"}
iex(9)> Map.has_key? defaults, :fg
true
iex(10)> Map.has_key? defaults, :ssg
false

iexから h ... で説明も得られる. popの返り値は 値と新しいmapのタプルだ. キーがあれば, popで取得した key, valueのセットを除いたmapを返してくれる. キーが見つからなければそのまま, 値は第三引数を返す(省略するとnilね).

iex(11)> h Map.pop

                       def pop(map, key, default \\ nil)

  @spec pop(map(), key(), value()) :: {value(), map()}

Returns and removes the value associated with key in map.

If key is present in map with value value, {value, new_map} is returned where
new_map is the result of removing key from map. If key is not present in map,
{default, map} is returned.

## Examples

    iex> Map.pop(%{a: 1}, :a)
    {1, %{}}
    iex> Map.pop(%{a: 1}, :b)
    {nil, %{a: 1}}
    iex> Map.pop(%{a: 1}, :b, 3)
    {3, %{a: 1}}

マップのパターンマッチ

前述の Map.pop を使わなくても, パターンマッチで値を取得できる. ただし キーがなければそこでエラー終了してしまう..

iex(12)> %{ fg: col } = defaults
%{bg: "white", fg: "black", font: "Arial"}
iex(13)> col
"black"
iex(14)> %{ bgm: col } = defaults
** (MatchError) no match of right hand side value: %{bg: "white", fg: "black", font: "Arial"}

マップの更新

Elixirは Immutableな変数を扱うので, 更新処理は右辺で行う。すなわち、もともとのmap変数の中を上書きすることはなく、変更した結果のマップを新たに左辺の変数 or パターンで束縛する(?束縛の意味があやしい)。

※パターンマッチ(左辺をマップ式で受ける?)で、キーワードを設定すれば それを含むものだけがマッチする。複数指定も可能。

構造体

Mapの制限を付与したデータ構造。構造体にすると, Mapに比べて以下の制限がある

  • キーに使えるのはアトムだけ
  • キーはフィールドと呼び?、定義時に初期値を設定できる
  • module内で宣言し, 固有の関数を定義する(クラスのようなもの?)
defmodule ImageHeader do
  defstruct magic: 0xCafeBabe, length: <<0::size(32)>>, body: <<>>
  def set_magic(base=%ImageHeader{}, <<value>>), do: %ImageHeader{base | magic: <<value>>} 
  def is_valid_magic(%ImageHeader{magic: val}), do: val == 0xDeadBeef
end

iex(3)> a = %ImageHeader{}
%ImageHeader{body: "", length: <<0, 0, 0, 0>>, magic: 3405691582}                                       
iex(4)> ImageHeader.is_valid_magic(a)
false
iex(5)> aa = %ImageHeader{a | magic: 0xdeadbeef}
%ImageHeader{body: "", length: <<0, 0, 0, 0>>, magic: 3735928559}                                       
iex(6)> ImageHeader.is_valid_magic(aa)
true

構造体の入れ子

入れ子記述は容易に気付けるだろう。問題は階層深くのメンバへのアクセス方法。変更を加える場合には、元のデータを渡していく必要があるので、階層分すべての構造体を理解してマッチングして...記述しなければならない.

簡略化のためのマクロ・関数が用意されている。

put_in(var.var1.var2, "new value")

ここで

  • var1: なにか(strucnt2)の構造体を束縛した、構造体struct1の変数
  • var2: struct2の変数名

関数を渡して引数に元の値、結果に書き換えてくれるマクロ update_in()がある. 取得するための get_in(), 取得して更新する get_and_update_in(),...

ヘルプを見て あとは実践(TODO)..

最後に書かれていたダークサイド...

この構造体や moduleに閉じ込めた 関数について、オブジェクト指向ぽく記述ができそう、というのはよろしく無いとある。まさにそう考えていただけにガツーンですよまったく。

Elixirは関数型言語、その特徴・メリットを知る(第九章、1ページ)

第9章 型とはなにか

失敗(TODO)

構造体名のとおり、本来はバイナリを扱いたかった。 c++のクラスとは異なり、すべて static method相当になってしまうのかな. thisポインタの代わりに 明示的にインスタンスを受けて, 変更後のインスタンスを返すような作りになりそう. 何らかの表現を返すなら タプルにすれば解決するだろう...

値をもらって バイナリ列に変換したいだけなのかな. ライブラリとかありそう.

defmodule ImageHeader do
  defstruct magic: <<0 :: size(32)>>, length: <<0::size(32)>>, body: <<>>
  def set_magic(base=%ImageHeader{}, <<value>>), do: %ImageHeader{base | magic: <<value>>} 
  defp set_magic_oct(value) when value >= 256, do: (
    <<rem(value,256) , set_magic_oct( div(value, 256) )>>
  )
  defp set_magic_oct(value) when value < 256, do: <<value>>
  def set_magic(base=%ImageHeader{}, value), do: (
    %ImageHeader{base | magic: set_magic_oct(value)}
  )
  def is_valid_magic(%ImageHeader{magic: val}), do: val == <<0xDe,0xad,0xBe,0xef>>
end

プログラミングElixir第7章~リストと再起

  • リストの再帰的構造
  • リストの横断と構築
  • アキュムレータ
  • mapとreduceの実装

ヘッドとテイル

リスト中、縦棒をはさむことでリストの結合を表現できる?。今までの表現では、縦棒が出てこなかったけれども省略して書いていたと思えばよいだろう。 Lispのリストがこんなのだった気がする。要素1つと次へのポインタみたいな塊(間違ってたらごめんなさい)

iex(1)> [1,2,3,4]
[1, 2, 3, 4]
iex(2)> [ 1 | [ 2 | [ 3 | [ 4 | [] ] ] ] ]
[1, 2, 3, 4]
iex(3)> [h|t]=[ 1 | [ 2 | [ 3 | [ 4 | [] ] ] ] ]
[1, 2, 3, 4]
iex(4)> h
1
iex(5)> t
[2, 3, 4]
iex(6)> [h|t] = [1,2,3,4]
[1, 2, 3, 4]
iex(7)> h
1
iex(8)> t
[2, 3, 4]

[head|tail] で受けるようだけれど, tailに残りすべてが放り込まれる. 要素数ゼロだとエラーになる。縦棒があるので要素数が1つはあることをマッチング条件としているからかな..

iex(11)> [h|t]=[1]
[1]
iex(12)> h
1
iex(13)> t
[]

iex(14)> [h|t]=[]
** (MatchError) no match of right hand side value: []

リストの構築

defmodule TestList do
  def len([]), do: 0
  def len([_head|tail]), do: 1 + len(tail)
end
iex(15)> TestList.len( [ 4, 5, 67, 333] )
4

※関数引数名を "_" で始めると、その変数は未使用であることを宣言する。未使用引数はコンパイル時に警告が出る。

引数に渡された数値リストを、要素の値を二乗したリストを返す関数を考える:

defmodule TestList do
  def squre([]), do: []
  def squre([head|tail]), do: [ head*head, squre(tail)]
end
iex(3)> TestList.squre( [1,2,3,4])
[1, [4, [9, [16, []]]]]

リストになっていない... カンマで区切ると, squre()の返り値を1つの要素としてしまうのね.

defmodule TestList do
  def squre([]), do: []
  def squre([head|tail]), do: [ head*head | squre(tail)]
end
iex(5)> TestList.squre( [1,2,3,4])
[1, 4, 9, 16]

なるほど, 縦棒は偉大なり..

map関数の作成

Enum.map が存在するが、これを自作してみようという話. リストの要素ごとに 任意の関数を呼び出して結果をリストとして返す. squreの場合は fn (x) -&gt; x*x end を実行させたようなものだ.

defmodule TestList do
  def map([], _func), do: []
  def map([head|tail], func), do: [ func.(head) | map(tail, func)]
end
iex(8)> TestList.map [1,2,3], &(&1*&1)
[1, 4, 9]
iex(9)> TestList.map [1,2,3], fn (x) -> x*x end
[1, 4, 9]

なるほど、素直に作れますね... 自作の場合、マッチングに制限をかけるとかして 想定外の結果を返さないようにするなどできそうね.

再起中の値の保持

上までは リストを受けて同じ要素数のリストを返す方法をみてきた. リスト全体の要素を見て、結果を返すことを考える. 簡単な例で数列の和を返す sum() 関数を実装する.

defmodule TestList do
  def sum([], total), do: total
  def sum([head|tail], total), do: sum(tail,head+total)
end

iex(2)> TestList.sum([1,2,3,4,5,6,7,8,9,10],0)
55

なるほど, よさそうだ. リストの前方から順に結果を得られる場合(リスト後方の値に依存しない場合)には有用そうだ. 数列だけではなくいろいろと妄想がはかどるが, 基本的には 状態 を 引数として渡すしかないようである.

これには 連想配列やキーワードリストが効果的な気がするね..

次にこれをリファクタリングする:

defmodule TestList do
  def sum(list), do: _sum(list,0)

  defp _sum([], total), do: total
  defp _sum([head|tail], total), do: _sum(tail,head+total)
end

モジュール外から使用するときに, 状態保持用の第二引数をいちいち記述する必要がないからだ。計算の途中から実行したいという需要はあるかもしれないが、それはそういうインタフェースを作ればよいだろう. デフォルトパラメータとして sum(list, total \\ 0) としないのは、内部でのみ扱う 状態 を外に意識させないためだろう.

プライベート関数の宣言に defp を使っていることに注目するとよい.

やってみよう

状態が1つであれば そのまま関数の返り値として扱えるかな。関数の処理成功/失敗や

defmodule TestList do
  def sum([]), do: 0
  def sum([head|tail]), do: (
    head + sum(tail)
  )
end

iex(3)> TestList.sum( [1,2,3,4,5,6,7,8,9,10])
55

Enum.reduce の差異発明

sumの場合, 2つの値の和を返す処理を実装していた。この部分を汎用化すると, 関数を渡すことになるだろう。

defmodule TestList do
  def reduce([], value, _func), do: value
  def reduce([head|tail], value, func), do: reduce(tail, func.(head,value), func)
end

iex(2)>TestList.reduce [1,2,3,4,5], 0, fn m,n -> m+n end
15
defmodule People do
  def males([]), do: []
  def males([[name, :male, age]|tail]), do: [[name, :male, age] | males(tail)]
  def males([_|tail]), do: males(tail)

  def test_data do
  [
    ["YNK",  :male,   18],
    ["John", :male,   21],
    ["Mary", :female, 19],
    ["Beck", :male,   32]
  ]
  end
end

性別を選べるようにしよう:

defmodule People do
  def filter([], _sex), do: []
  def filter([[name, sex, age]|tail], sex), do: [[name, sex, age] | filter(tail,sex)]
  def filter([_|tail], sex), do: filter(tail,sex)
# ... test_data
end

関数の引数で与えた値が パターンマッチにも適用できている... 後方で与えている引数なのに, 前方のマッチングにも使われる.のか..

iex(5)> filter test_data,  :male
[["YNK", :male, 18], ["John", :male, 21], ["Beck", :male, 32]]
iex(6)> filter test_data,  :female
[["Mary", :female, 19]]
iex(7)> filter test_data,  :none
[]
defmodule People do
  def filter([], _sex), do: []
  def filter([head=[_, sex, _]|tail], sex), do: [ head | filter(tail,sex)]
  def filter([_|tail], sex), do: filter(tail,sex)
# ... def test_data do
end

リストのマッチングを head=で受けている。名前付き前方参照みたいな書式で、マッチングしたリストを 関数内で headとして参照できる. いちいち関数引数のパラメタを書き直す必要がないので 便利が良い.

やってみよう

fromからtoまでの数のリストを返す関数を書いてみる.

defmodule Test do
  def span(from,to) when from<to, do: [from | span(from+1,to)]
  def span(to,to), do: [to]
  def span(_from,_to) when _from>_to, do: []
end
iex(4)> Test.span(4,3)
[]
iex(5)> Test.span(4,4)
[4]
iex(6)> Test.span(2,6)
[2, 3, 4, 5, 6]

whenを使っていいのかな..

Listモジュール

ざっくりと紹介されている. helpで眺めるとかするとよいか.

困ったら 本家 を見るとよさそうね..

気づき

2020/06/19

学習中に気づいたこととか覚えておいたほうがよさそうなこと

語彙

アリティ arity

関数末尾にある /n のこと. 引数の数.

引数の数が異なる同名の関数が定義できるので、関数への参照?を得るためには arityを指定して唯一性を担保しなくてはいけないようだ.

内包表記 (comprehension)

ローカル変数を定義して、それにマッチするリストを与え、すべての組み合わせに対して 関数を評価した結果のリストが得られる。

for value <- range, cond, do: expression

バイナリを扱う場合の例

iex(1)> pixels = <<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>
iex(2)> for <<r::8, g::8, b::8 <- pixels>>, do: {r, g, b}
[{213, 45, 132}, {64, 76, 32}, {76, 0, 0}, {234, 32, 15}]

ジェネレータ節

cond式

プロトコル

ビヘイビア

ファイル拡張子の識別

拡張子 概要
*.exs scriptから直接実行する(実行時コンパイル?)
*.ex コンパイルしてバイナリを生成する

elixir関連ネタ

プログラミングElixir第6章

モジュールと名前付き関数

名前付き関数を定義するには、その親になるモジュールを定義する必要がある。モジュールなくしては名前付き関数を定義できない。

FILE: times.exs

defmodule Times do
  def double(n) do
    n * 2
  end
end

関数のボディ記述

以下の三種類すべて同じ記述方法。 "do: block" が基本で、あとはシンタックスシュガーぽい。"()"はブロックの記述か。

def double(n), do: n*2
def double(n), do
  n*2
end
def double(n), do: (
  n*2
)

練習問題

重複するので割愛。コンパイル方法:

  • iex 引数でファイルを渡す
  • iex>プロンプトから c "filename.exs"

関数呼び出しとパターンマッチ

defmodule Factorial do
  def of(0), do: 1
  def of(n), do: n * of (n-1)
end

iex内でも module定義できますね..

iex(8)> defmodule Factorial do
...(8)>   def of(0), do: 1
...(8)>   def of(n), do: n * of (n-1)
...(8)> end
{:module, Factorial,
 <<70, 79, 82, 49, 0, 0, 4, 60, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 130,
   0, 0, 0, 15, 16, 69, 108, 105, 120, 105, 114, 46, 70, 97, 99, 116, 111, 114,
   105, 97, 108, 8, 95, 95, 105, 110, 102, ...>>, {:of, 1}}
iex(9)> Factorial.of 3
6
iex(10)> Factorial.of 7
5040
iex(11)> Factorial.of 10
3628800

関数定義順にパターンマッチが試みられるようである。先食いするような記述はElixirが警告を出す(後に定義した関数が呼ばれることがない旨)。

ガード節

関数の引数をチェックする。パターンマッチでは型まで判定できないので、whenキーワードを使う:

defmodule Factorial do
  def of(0), do: 1
  def of(n) when is_integer(n) and n>0, do: n * of (n-1)
end

ここでは 自然数であることを確認している。 is_*() で変数の型を知ることができる。タブ補完してみると、以下が見える:

iex(1)> is_
is_atom/1         is_binary/1       is_bitstring/1    is_boolean/1
is_float/1        is_function/1     is_function/2     is_integer/1
is_list/1         is_map/1          is_nil/1          is_number/1
is_pid/1          is_port/1         is_reference/1    is_tuple/1
Erlang/OTP 21 [erts-10.2.4] [source] [64-bit] [smp:48:48] [ds:48:48:10] [async-threads:1]
Interactive Elixir (1.7.4) - press Ctrl+C to exit (type h() ENTER for help)

デフォルトパラメータ

デフォルトパラメータは バックスラッシュ二つ( \ ) をパラメータ変数の後ろにおいて、値を続ける。

パラメータのマッチングは先頭から順次マッチングがとられる。必須パラメータのマッチングが行われ、不足する場合はエラーとなる。デフォルトパラメータが複数あり、呼び出し時に一部が渡される場合は、デフォルトパラメータが定義されている変数の先頭から順に適用される。

defmodule TestModule do
  def func( p1, p2 \\ 2, p3 \\ 3, p4 ), do: IO.inspect [p1,p2,p3,p4]
end
iex(4)> TestModule.func  11,22,33,44
[11, 22, 33, 44]
[11, 22, 33, 44]
iex(5)> TestModule.func  11,22,33
[11, 22, 3, 33]
[11, 22, 3, 33]
iex(6)> TestModule.func  11,22
[11, 2, 3, 22]
[11, 2, 3, 22]
iex(7)> TestModule.func  11

練習問題

iex(13)> c "guess.exs"
warning: redefining module TestModule (current version defined in memory)
  guess.exs:1

[TestModule]
iex(14)> TestModule.guess 273 , 1..1000
Is it 500
Is it 250
Is it 375
Is it 312
Is it 281
Is it 265
Is it 273
finish!
:ok

if文つかわずに... ということでちょっと型チェックが緩いけれどこんな感じ? FILE: guess.exs

defmodule TestModule do
  def get_range(a,m,s,_) when a<m, do: s..m
  def get_range(a,m,_,e) when a>m, do: m..e
  def get_range(a,_,_,_), do: a..a

  def guess(actual, s..e) when s<e, do: (
    m = div(s+e, 2)
    IO.puts "Is it #{m}"
    guess(actual, get_range(actual, m , s, e))
  )
  def guess(_,_), do: IO.puts "finish!"

end

プライベート関数

スコープがモジュールに収丸場合、 def ではなく、 defp を用いる。同じ関数名で 混在することはできない(同じスコープにしないとマッチングが取れないよね)

パイプ演算子( |&gt;

関数の返り値を、次の関数の第一引数に取るとき、パイプ演算子が使える。

func1 |> func2

モジュール

  • 名前空間を提供してくれるだけ。
  • 入れ子にできるが、内部的には一階層しかなく、モジュール名が長くなるだけ(Ex. "Module1.Module2"という名になる)

import ディレクティブ

モジュール名を引数にして import ModuleName と記述する。 ModuleName.func のモジュール名を記述せず、 func と書ける。

alias ディレクティブ

入れ子になったモジュール名を、任意の代替名で参照できる。

alias Module.My.Device, as: MyDev
alias Module.My.Device, as: Device
alias Module.My.Device

下の二つは同じ意味で、as:を省略すると、モジュール名の最後のドット以降をaliasとして宣言したことになる。

require ディレクティブ

モジュールで定義したマクロを使うときに、requireしておくといいらしい。

TODO: マクロの勉強

モジュールの属性

モジュール定義のスコープ内で、以下のように宣言する。モジュール内の関数から @name で参照できる。

@name value

属性 name の値 value, と読み, おおむねモジュールによって定まる定数や authorなどの文字列を記述するのが慣例のようだ.

モジュールの名前:Elixir, Erlang

モジュール名は、内部処理でアトム :"モジュール.サブモジュール...." と表現され、関数呼び出しには、アトム+ "." + モジュールの関数 として扱われる。

Erlangの関数を呼び出す場合は、Elixirのモジュール名がCamelCaseであるのと対照的に、小文字で始まる。これによりElixirとErlangのモジュールを区別しているようである.

Erlangの io モジュール, format関数を呼ぶ例:

:io.format("format is not equals to C lang.~n")