演算子=へんな関数
連載第4回目の今回は、
その前に、
なんだか難しそうな定義がありそうな気がしますが、
> operators are just funny looking function calls
[1]
「ただの変な見た目の関数呼び出し」。たしかに言われてみると、1 + 1
ではなく(+1 1)
と書けば事足りますし、3 + 2 * 4
を計算したければ必ず(+ 3 (* 2 4))
と書くので優先順位の問題もありません。
実際、Lisp in
ほげほげでほげほげ言語を検索すると、
しかし、E = m * c ** 2
と書ける言語と、(setq E (* m (** c))
しかし演算子を再定義したり、
二項演算子
前口上はこれくらいにして、
struct Point {
var x:Int
var y:Int
}
をPrintable
にしたところで終わっていました。これをEquatable
にしてみます。equatableということは、==
を実装すればいいわけです。こういうふうに。
func ==(p:Point, q:Point)->Bool {
return p.x == q.x && p.y == q.y
}
こんな簡単でいいのでしょうか?
いいみたいです

二項演算子
前置と後置の場合は?
二項演算子の場合、++
は前置と後置両方ありますよね。
前述のPoint
を++
したいケースというのはあまり考えられないのですが、x
とy
それぞれ++
するということにして、
前置のほうは簡単です。引数を上書きするので、inout
がついている点に注意してください。
prefix func ++(inout p:Point)->Point{
p.x++; p.y++; return p
}
後置のほうはちょっと工夫がいります。返り値は++
する前の値なので、
postfix func ++(inout p:Point)->Point {
var q = p
p.x++; p.y++; return q
}
それではうまくいくでしょうか。うまくいったみたいですvar
ではなくlet
で宣言された定数の場合、

オレオレ演算子の書き方
ここまでは、
たとえばこんなのはいかがでしょう?
infix operator => { associativity left precedence 95 }
func => <A,R> (lhs:A, rhs:A->R)->R {
return rhs(lhs)
}
f(a)
の代わりに、a => f
と書けるようになります。
これの何がうれしいかというと、printf
デバッグならぬprintln
デバッグの時。Swiftにはplaygroundがあるのでprintlnデバッグしなければならないケースは他より減るとはいえ、println(
と書いて)
で閉じるより、=> println
と付け加えた方が楽ですし直感的でもあります。
見てのとおり、(pre¦in¦post)fix operator
で演算子を定義しておく必要があります。続く{}
の中で、=>
の重ね打ちも図3のような感じでできたりします。

演算子に関しては、
The (Un)?documented Feature
それではどのような記号が演算子として使えるのか。公式ドキュメントにはこう書いてあります

U+の羅列、
import Foundation
prefix operator √ {}
prefix func √(x:Double)->Double {
return sqrt(x)
}
√2
// U+2211, N-ARY SUMMATION
// not U+3A3, GREEK CAPITAL LETTER SIGMA
infix operator Σ { }
func Σ (r:Range<Int>, f:Double->Double)->Double {
return Array(r).map{f(Double($0))}.reduce(0,+)
}
let s = 1...10 Σ {$0}
s
// U+220F, N-ARY PRODUCT
// not U+3A0, GREEK CAPITAL LETTER PI
infix operator Π { }
func Π (r:Range<Int>, f:Double->Double)->Double {
return Array(r).map{f(Double($0))}.reduce(1,*)
}
let p = 1...10 Π {$0}
p

1つ注意したいのは、
記号が使えることはかつては公然の秘密でしたが、
さらに次のコードを追記してみてください。
for n in 1...16 {
let e_1 = 1...n Σ {1/(1...Int($0) Π {$0})}
println( exp(1) - e_1 )
}
どんどん1
に近づいています。どこかで見たことがありませんか? そう、e
の定義です。
指数関数exp(x)はテイラー展開で

と書けますが、1...
はexp(1) - 1
に相当するわけです。なぜexp(1)そのものではないかというと、Π
は、0!
が扱えないから。第0項がない分、
ここまでくるといっそ後置の!
を定義してn!
とかやりたい誘惑に駆られますが、n!
はImplicitly Unwrapped Optionalsのために予約されていて再定義不可能です。
まとめ
Lispを見ればわかるとおり、
そして一定以上の人気を得るのに欠かせないいまひとつの機能が、
本誌最新号をチェック!
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の実行環境 (基礎編)