関数という名の(定|変)数
連載第1回目
- 変数に代入できる
- 関数の引数にできる
- 関数を返す関数が書ける
JavaScript、
var fizzbuzz = function(n) {
if (n % 15 == 0) { return "FizzBuzz"; }
if (n % 5 == 0) { return "Buzz"; }
if (n % 3 == 0) { return "Fizz"; }
return n;
}
for (var i = 1; i <= 100; i++) {
console.log(fizzbuzz(i))
}
見てのとおり、fizzbuzz
は変数名で、
var fizzubzz = func(n:Int)->String {
if n % 15 == 0 { return "FizzBuzz" }
if n % 5 == 0 { return "Buzz" }
if n % 3 == 0 { return "Fizz" }
return String(n)
}
いいえ、
var fizzbuzz = { (n:Int)->String in
if n % 15 == 0 { return "FizzBuzz" }
if n % 5 == 0 { return "Buzz" }
if n % 3 == 0 { return "Fizz" }
return String(n)
}
あるいはこうです。
var fizzbuzz:(Int)->String = { n in
if n % 15 == 0 { return "FizzBuzz" }
if n % 5 == 0 { return "Buzz" }
if n % 3 == 0 { return "Fizz" }
return String(n)
}
まとめるとこう。
- funcは不要
- →
{ 引数 in 定義 }
という形をしている
- →
- 型指定は必要
- →前者は
{型定義付き引数 in 定義}
- →後者は
変数名:型 = { 引数名 in 定義 }
- →前者は
変数指定を{}
の外ではなく中でやるあたり、
function range(start, end) { // ないので作る
var ret = [];
for (var i = start; i <= end; i++) ret.push(i);
return ret;
}
var a = range(1, 100).map(fizzbuzz)
console.log(a)
ここで、fizzbuzz()
を無名化するとこうなります。
var a = range(1, 100).map(function(n) {
if (n % 15 == 0) { return "FizzBuzz"; }
if (n % 5 == 0) { return "Buzz"; }
if (n % 3 == 0) { return "Fizz"; }
return n;
}))
動くことは動きますが、()
の中に入ったfunction
はなんとも不格好です。Rubyならどうでしょうか?
a = (1..100).map { ¦n¦
if n % 15 == 0 then "FizzBuzz"
elsif n % 5 == 0 then "Buzz"
elsif n % 3 == 0 then "Fizz"
else n.to_s
end
}
p a
「メソッドの最後の引数がブロックの場合、
var a = (1...100).map { n in
if n % 15 == 0 { return "FizzBuzz" }
if n % 5 == 0 { return "Buzz" }
if n % 3 == 0 { return "Fizz" }
return String(n)
}
println(a)
見てのとおり、map
の後の無名関数を()
でくくらなくても動きました。無名関数の引数と戻り値のほうも指定していません。このあたりは、
しかし、
var a = (1...100).map {
if $0 % 15 == 0 { return "FizzBuzz" }
if $0 % 5 == 0 { return "Buzz" }
if $0 % 3 == 0 { return "Fizz" }
return String($0)
}
println(a)
なんと、in
が消えてしまいました。その代わりn
があった位置に$0
という未宣言の変数が存在します。この変数のことをプレイスホルダー$0
が最初の引数、$1
が次の引数といった具合です。これ、
{}は全部関数!
ここで今まで見てきたSwiftのコードの{}
をじーっとよく見てみてください。何か見えてきませんか? ヒントを1つ。if
の後ろの{}
は、if
の後ろの条件には()
がないのに……。
そう。Swiftでは、{}
はブロックで、
本当かどうか、if
を再発明して!
func IF(PRED:()->Bool,THEN:()->(),ELSE:()->()) {
[true:THEN, false:ELSE][PRED()]!()
}
func fact(n:Int)->Int {
var ret:Int!
IF( {n <= 1},
{ ret = 1 },
{ ret = n * fact(n - 1)}
)
return ret
}
println(fact(10))

動いてますIF
は関数で、IF
はずいぶん奇妙きてれつな形をしています。ちょっと冗長に書き直してみましょう
func IF(
PRED:()->Bool, // 引数なしでBoolを返す関数
THEN:()->(), // 引数なし、戻り値なしの関数
ELSE:()->() // 引数なし、戻り値なしの関数
) {
let dict = [ // [Bool:()->()] な辞書
true:THEN, // true には THEN を
false:ELSE // false には ELSE を紐付け
]
let which = dict[PRED()] // PRED()の結果で辞書引き
which!() // それを実行
}
きてれつではありますが、
Swiftが{}
を使っていないこともあります。Swiftでは辞書リテラルは[key:value]
の形式をとるので{}
が辞書リテラルなのかブロックなのかあいまいになることはありません。{}
を見たら例外なくブロック、
ではfuncは不要かというと……
ここまで見てきたとおり、{}
にあり、func(){}
という構文糖衣は無用の長物に思えます。しかしこれが必要になるケースが2つほど存在します。
1つは、fact
も再帰的に定義されています。ここでもう一度
func fact(n:Int)->Int {
return n <= 1 ? 1 : n * fact(n - 1)
}
これを、
let fact:(Int)->Int = { n in
return n <= 1 ? 1 : n * fact(n - 1)
}
println(fact(10))
一見動きそうですが、
<EXPR>:9:29: error: variable used within its own
initial value
return n <= 1 ? 1 : n * fact(n - 1)
これを防ぐためには、var
、
var fact:(Int)->Int
fact = { n in
return n <= 1 ? 1 : n * fact(n - 1)
}
なんとも冗長であるうえに、fact
を上書きできてしまいます。残念ながら"use strict"されていない
)arguments.
やR言語のRecall()
相当の、func
しましょう。どうしてもという方のために、
func recall<T,R>(f:((T->R),T)->R)->T->R {
var r:(T->R)!
r = { n in f(r,n) }
return r
}
let fact = recall { $1 <= 1 ? $1 : $1 * $0($1-1)
}
という手法も一応紹介だけしておきます。何を意味するかは、
ただし、func
が必要になる実例がもう1つ出てきました。総称関数
func add(x:Int, y:Int)->Int { // 0
return x + y
}
func add(x:String, y:String)->String { // 1
return x + y
}
println(add(4, 2)) // 6
println(add("4", "2")) // "42"
まったく同じ名前、
そこで総称関数です。
func add<T>(x:T, y:T)->T {
return x + y
}
add(4, 2) // 6
add("4", "2") // 42
C++のテンプレートやJavaのジェネリクスに相当するこの機能は、add(4, 2)
では4
と2
からT
と推論され、は
Intadd(x:Int, y:Int)->Int
がコンパイラーによって生成され、add(4,2)
ではadd(x:String,y:String)->Int
が生成されるというわけです。
しかし、
let add:<T>(x, y)->T = { x, y in
return x + y
}
なぜそう書けないのか。次回はSwiftにおける総称関数を詳しくみていきます。
本誌最新号をチェック!
Software Design 2022年9月号
2022年8月18日発売
B5判/
定価1,342円
- 第1特集
MySQL アプリ開発者の必修5科目
不意なトラブルに困らないためのRDB基礎知識 - 第2特集
「知りたい」 「使いたい」 「発信したい」 をかなえる
OSSソースコードリーディングのススメ - 特別企画
企業のシステムを支えるOSとエコシステムの全貌
[特別企画] Red Hat Enterprise Linux 9最新ガイド - 短期連載
今さら聞けないSSH
[前編] リモートログインとコマンドの実行 - 短期連載
MySQLで学ぶ文字コード
[最終回] 文字コードのハマりどころTips集 - 短期連載
新生「Ansible」 徹底解説
[4] Playbookの実行環境 (基礎編)