1. ホーム
  2. erlang

[解決済み】Elixirにはなぜ2種類の関数があるのですか?

2022-04-18 12:55:59

質問

Elixirを学んでいるのですが、なぜ2種類の関数定義があるのでしょうか?

  • で定義された関数は def を使用して呼び出されます。 myfunction(param1, param2)
  • で定義された無名関数。 fn を使用して呼び出されます。 myfn.(param1, param2)

2番目の種類の関数だけが、第一級のオブジェクトであり、他の関数にパラメータとして渡すことができるようです。モジュールの中で定義された関数は fn . のような構文があります。 otherfunction(&myfunction(&1, &2)) しかし、そもそもなぜそれが必要なのでしょうか?なぜ otherfunction(myfunction)) ? Rubyのように括弧なしでモジュール関数を呼び出せるようにするためだけなのでしょうか?モジュール関数やfunを持つErlangからこの特徴を受け継いでいるようですが、実はErlang VMの内部動作に由来するのでしょうか?

2種類の関数があり、他の関数に渡すために1つの型から別の型に変換することに何か利点がありますか?関数を呼び出すのに2つの異なる表記があることにメリットはあるのでしょうか?

どのように解決するのですか?

ネーミングをはっきりさせておくと、どちらも関数です。一方は名前付き関数で、もう一方は匿名関数です。しかし、あなたの言うとおり、両者の動作は多少異なっています。なぜそのように動作するのかを説明します。

まずは2つ目から。 fn . fn はクロージャであり lambda をRubyで作成しました。次のようにして作ることができる。

x = 1
fun = fn y -> x + y end
fun.(2) #=> 3

関数は複数の節を持つこともできます。

x = 1
fun = fn
  y when y < 0 -> x - y
  y -> x + y
end
fun.(2) #=> 3
fun.(-2) #=> 3

さて、次は違うことをやってみよう。異なる数の引数を期待する異なる節を定義してみよう。

fn
  x, y -> x + y
  x -> x
end
** (SyntaxError) cannot mix clauses with different arities in function definition

いや~、エラーになっちゃいましたね。異なる数の引数を期待する節を混在させることはできません。関数は常に固定されたアリティを持っています。

では、名前付き関数について説明しましょう。

def hello(x, y) do
  x + y
end

予想通り、これらは名前を持ち、またいくつかの引数を受け取ることができます。しかし、これらはクロージャではありません。

x = 1
def hello(y) do
  x + y
end

このコードはコンパイルに失敗します。 def の場合、変数スコープが空っぽになります。そこが両者の重要な違いです。特に、名前付き関数はそれぞれまっさらな状態から始まるので、異なるスコープの変数がごちゃごちゃにならない点が気に入っています。明確な境界があるのです。

上の名前付きhello関数を無名関数として取り出すことができました。あなた自身がそう言っていましたね。

other_function(&hello(&1))

そして、なぜ単純に hello 他の言語と同じように?それは、Elixirでは関数が名前によって識別されるからです。 のアリティです。つまり、2つの引数を受け取る関数と3つの引数を受け取る関数は、たとえ同じ名前であったとしても別の関数になるのです。つまり、単に hello を使用すると、どの hello ということです。引数が2つのもの、3つのもの、4つのもの?これは、異なるアリティを持つ節を持つ無名関数を作れない理由と全く同じである。

Elixir v0.10.1以降では、名前付き関数を捕捉するための構文が用意されています。

&hello/1

これで、アリティ1のローカルな名前付き関数helloが捕捉されます。この言語とそのドキュメントでは、関数をこのように識別することが非常に一般的です。 hello/1 の構文があります。

Elixirが無名関数の呼び出しにドットを使うのもこのためです。を単純に渡すことはできないからです。 hello そのため、名前付き関数と匿名関数は自然に区別され、それぞれを呼び出すための明確な構文が、すべてをより明確にします(Lispの人なら、Lisp 1とLisp 2の議論のために、このことをよく知っているでしょう)。

全体として、以上が2つの関数がある理由と、それらの動作が異なる理由です。