魂の生命の領域

AWS とか Python とか本読んだ感想とか哲学とか書きます

無名関数がよくわからないので複数の言語で比較する

概要

  • lambda 式がよく分かってない頃に書いた記事
  • 今は Python についてはある程度使い慣れた
  • でもほかの言語についてはまだ何尾進歩していないのでスタート地点を示す意味でもここで晒す

目的

  • 複数の言語で無名関数の定義を比較して共通するイメージがあればそれを得る

現状

  • 無名関数がよくわからん
  • 無名関数? 匿名関数? Lambda 式? 関数リテラルクロージャ
  • 「あっ、ここ無名関数使えばいい感じになるぞ」の感覚を得たい
  • 現時点では一度きりしか使わない処理を書くときに便利、という程度の認識

複数の言語で比較してみる

  • 名前の付いた関数を定義する際はどの言語でも(型の宣言の有無とかを除けば)だいたい同じだと思います。
  • しかし無名関数に限っては言語ごとに独自のルールが際立つのでよくわからんという感じがあるように思えます。

Python の場合

  • Python の場合はガッツリ lambda という単語が入ってきます。
lambda 変数名: 処理
  • いくつかサンプル
>>> def my_add(a):
...     return lambda x: x + a
...
>>> f = my_add(1)
>>> f(100)
101
>>> my_add(1)(2)
3
>>> (lambda s: 'hoge'+ s)('fuga')
'hogefuga'
  • よく高階関数map とか filter とか)の第一引数に入れたりしますよね

Go の場合

  • Go の場合は関数リテラルという名前がついています。
  • 関数リテラルは定義の外にある変数にアクセスできます。
func(変数名 変数の型) 戻り値の型 {
    // 処理
    return 戻り値
}
  • 通常の関数の定義がこれなので、なるほど無名関数、という感じです。
func hoge(変数名 変数の型) 戻り値の型 {
    // 処理
    return 戻り値
}
  • いくつかサンプル
package main

import "fmt"

func main() {
    x := 10

    // 宣言して即呼び出すパターン
    func(i int) {
        // 定義の外にある変数 x にアクセスできる
        fmt.Println(i * x)
    }(2)

    // 定義だけしておくパターン
    f := func(s string) int {
        return len(s)
    }

    // ここで呼び出す
    fmt.Println(f("hogege"))
}
  • mumei.go という名前を付けて実行するとこうなります。
$ go run mumei.go
20
6
  • 上の例で書いた 定義だけしておくパターン って「無名なのに名前つけてるやん」って思ってしまったのですがどうなんでしょう。よくわかりません
  • 関数として定義するか関数オブジェクトとして定義するかの違いみたいなものなんでしょうか?
    • でも Go はクラスからオブジェクト指向ではないのでそれもまた違うのかなぁ…とよくわからなくなっている
    • てか関数オブジェクトじゃなくて関数リテラルか。
  • たぶんクロージャとしての性質が重要なんだよねたぶん

JavaScript の場合

  • あまり意識せずに無名関数使ってる言語ランキング1位(勝手なイメージ)
  • JavaScipt と JQuery の根っこの違いを私自身が分かっていないので Web 風味な使い方には触れずに書きます。
function(引数名, 引数名, ...) {
  // 処理
}
  • 使うとすればこんな感じです。
var my_add = function(a, b){
  return a + b;
}

var hoge = my_add(1, 2) //3
  • あとは Go のときのように宣言時に引数を代入して実行することもできます。
var my_add = (function(a, b){
  return a + b;
})(1, 2)

console.log(my_add); //3
  • 定義するときは必ず上のように変数に代入するか、関数の引数として書いてやる必要があります。
    • var で宣言しているところからもわかるように変数として宣言するからだと思います。

Elm の場合

  • まだ文法をかじっている途中なのですがせっかくなので書きます。
\引数名 引数名 ... -> 式
  • そもそも関数の定義のしかたが以下のようになっているのでこれもなるほど感があります。
関数名 引数名 引数名 ... = 式
  • REPL でいくつか試してみます。
> \x y -> x + y
<function> : number -> number -> number
> (\x y -> x + y) 1 2
3 : number
  • 無名関数に名前を与えることで関数の定義とすることもできるみたいです。
> myAdd = \x y -> x + y
<function> : number -> number -> number
> myAdd 1 2
3 : number
  • ん~、関数型っぽいね~うんうん(ニワカ)

Haskell の場合

  • せっかく関数型言語について取り上げたので Haskell の場合も簡単に見てみます。
\引数名 引数名 ... ->
  • 通常の関数の場合はこうです。
関数名 引数名 引数名 ... =
  • Elm と同じですね。いや逆か。
  • REPL で一応試してみます。
Prelude> (\a b -> a + b :: Int) 1 2
3
  • ::Intで戻り値の型を明示する方が安全です。
  • もう書きませんがこちらも無名関数に名前を付けて(等号で結ぶ)関数の定義とすることができます。

まとめ

  • こうして見ると複数の言語に手を出してる割に全然理解してないなというのがよくわかります(泣)。
    • いや、中途半端に手を出しているからこそなのだろう

課題

  • 定義と一番簡単な例しか見れていないので、使いどころまでは深堀りできませんでした。
  • 高階関数を使うときに重要とかそういうのがあった気がするのでそこからまた勉強します。
  • 言語を限定して掘り下げたいです。