こんなことできま<del>せん</del><ins>した</ins> Tuple 編

## はじめに

Egison ( http://www.egison.org/ ) をいろいろ触っていて、仕様や機能等で、今ひとつかゆいところに手が届かないなー、と感じることがたびたびあります。

そのうちの、tuple に関する操作。
例えば、「tuple を collection に変換する関数」。これが、標準では用意されていないのです。
またそもそも、↑を実現するのに必要そうな、「tuple の要素数を返す関数」とか、「tuple の任意の要素を返す関数」とかも用意されていません。

なんとかできないかなー、実現は難しそうだなー、と思っていたのですが。
色々実験しているウチに、実現する方法を見付けちゃいました。
ということで、その方法を紹介します。
書いてたらちょっと長くなっちゃったので、気長にお付き合いください。

## 開発環境・動作確認環境
+ Mac OSX 10.9.5
+ Egison 3.5.3/3.5.6[^1]

## 準備

まず、今回のコードの基礎となる3つの機能（新文法と、2つの primitive functions）について簡単に紹介・解説。

### 1. `#` と `%` を用いた部分適用

Egison 3.5.3 から追加された、新しい文法[^2]です。
「《引数の個数》#〜」で、lambda を簡単に定義できます。引数は1番目から順に「%1」「%2」… で参照できます。また自分自身を「#」で参照できます[^3]。
Egison の Change logs には、`(define $id 1#%1)`、`(define $fst 2#%1)`、`(define $snd 2#%2)` といった、今までにも定義されていた標準ライブラリ関数の再定義や、`(2#(+ (* 10 %1) %2) 1 2);=>12`[^4]、`(take 10 (1#{%1 @(# (* %1 2))} 2));=>{2 4 8 16 32 64 128 256 512 1024}`[^5] といった具体例も紹介されています。

私も勉強のために、↓こんなの作ってみました。
`(define $fibs (2#{%1 @(# %2 (+ %1 %2))} 1 1))`
`(take 10 fibs);=>{1 1 2 3 5 8 13 21 34 55}`
フィボナッチ数列の無限列が非常にコンパクトに定義できました(^-^)

### 2. `read` 関数

こちらは、元からある primitive function[^6] です。
簡単に言えば、「文字列を Egison のコードと見做して解釈する」ものです。例えば `(read "100");=>100`、`(read "{\"A\" \"B\" \"C\"}");=>{"A" "B" "C"}`、など。
ただし。スクリプト言語によくある `eval` と決定的に異なるのは、「コンテキストを引き継げない（指定できない）」ということ。
どういうことかというと。例えば。
`(let {[$n 100]} (read "n"))` としても、'Unbound variable: n' というエラーになってしまいます。`read` 関数の世界には外で定義した `n` は見えていない、ということです。
それどころか、`(read "(+ 1 2)")` すら 'Unbound variable: +' となります。primitive function も見えてないのです。計算もできません。
何なら大丈夫なのかというと、文法レベルで用意されているもの[^7]のうち、「トップレベルにしか書けないモノを除いたもの」のみ、のようです。
例えば、`(read "(lambda [$x] {x})")` はOKですが、`(read "(define $fn (lambda [$x] {x}))")` だと 'Parse error 《中略》unexpected reserved word "define" / expecting expression' と怒られます。
ちなみに。前節で紹介した部分適用の書式はOKです。
`(read "1#{%1}")` と書くと、先ほどの `(read "(lambda [$x] {x})")` と（機能的に）全く同じ lambda が返ってきます。

なお、その部分適用の書式は、引数の個数を表す`#`の前の数字や、引数の参照を表す`%`の後の数字に、変数を使用することはできません。
つまり。例えば「`n`個の引数を取る関数で、戻り値は`i`番目の引数」というつもりで `n#%i` と書いても、Parse error になってしまいます。
でも。`n` も `i` もきちんと定義された変数（数値が束縛されている）ならば、以下のように書くことはできます。
`(read (S.concat {(show n) "#%" (show i)}))`
`show` は引数の値を文字列に変換する関数（primitive function、read の逆の働き）、`S.concat` は文字列連結関数（標準ライブラリ関数）です。結果的に（例えば n:=3, i:=2 ならば）`3#%2` という lambda になります。

やりたいことの一つ、「tuple の任意の要素を返す関数」の実現方法がが見えてきましたね(^-^)
あと「tuple の要素数を返す関数」が実現できれば、これとの組み合わせでいけそうです。

### 3. `tuple?` 関数

こちらも、元からある primitive function[^6] です。
簡単に言えば「引数が tuple かどうかを真偽値で返す関数」です。

ちょっと待った。
Egison は、全ての値は実は tuple（多値）だそうです[^8]。他の言語で言うスカラー値も、collection などの他のオブジェクトも、実は要素数が1の tuple、というわけです。
だったら、この関数って常に `#t` を返しそうですよね？どういうことでしょう？

実はこの関数、引数を2つ受け取る関数なのです。
`(tuple? 2 [#t #f]); => #t`
`(tuple? 4 [1 2 3]); => #f`
`(tuple? 1 100); => #t`
`(tuple? 0 []); => #t`
`(tuple? [1 2]); => #t`
お分かりになりますでしょうか？　つまり、第2引数の tuple の要素数が、第1引数の数値と等しいときに `#t`、そうでなければ `#f` を返す関数なのです。
なお、↑最後のコードが `#t` となるのは、[前回記事](http://qiita.com/antimon2/items/5184282474970c52c97d) で触れた「`n`個の引数を受け取るよう定義された関数（lambda）は、`n`個の要素からなる tuple を1つだけ渡しても動作する」という挙動により `(tuple? 1 2)` となるので `#t` というワケ[^9]。

これを利用すれば、tuple の要素数も分かりますねo(^▽^)o

## 本題：tuple_util.egi 作ってみました。

前節までの準備を元に。
tuple を色々操作できるユーティリティ関数群を作ってまとめてみました。
これ以上順を追っていると無駄に長くなるのでまずコードから。

### コード

```clj:tuple_util.egi
;; Save this code as a "tuple_util.egi" file

;; tuple-to-XXX

(define $tuple-to-array
  (lambda [$t]
    (match nats0 (list integer)
      {[(loop $i [1 $n] <cons _ ...> <cons ?(tuple? $ t) _>)
        (generate-array [$i] n ((read (S.concat {(show n) "#%" (show i)})) t))]})))
; (define $tuple-to-array'  ; faster `tuple-to-array`
;   (lambda [$t]
;     (letrec {[$looper (lambda [$n]
;         (if (tuple? n t)
;           (generate-array [$i] n ((read (S.concat {(show n) "#%" (show i)})) t))
;           (looper (+ n 1))))]}
;       (looper 0))))

(define $tuple-to-collection
  (lambda [$t]
    (let {[$a (tuple-to-array t)]}
      (map 1#a_%1 (between 1 (array-size a))))))
      ; ↑ is shorthand of ↓
      ; (map (lambda [$i] a_i) (between 1 (array-size a))))))


;; XXX-to-tuple

(define $array-to-tuple
  (lambda [$a $s $e]
    ((read (S.concat {"1#[" @(map 1#(S.append " %1_" (show %1)) (between s e)) "]"})) a)))

(define $collection-to-tuple
  (match-lambda (list something) {
    [(loop $i [1 $n] <cons $x_i ...> <nil>) (array-to-tuple x 1 n)]}))


;; others

(define $tuple-size
  (lambda [$t]
    (array-size (tuple-to-array t))))
(define $T.size tuple-size) ;; alias

(define $tuple-nth
  (lambda [$n $t]
    (ref (tuple-to-array t) n)))
(define $T.nth tuple-nth) ;; alias

(define $tuple-car
  (lambda [$t]
    (ref (tuple-to-array t) 1)))
(define $T.car tuple-car) ;; alias

(define $tuple-cdr
  (lambda [$t]
    (let {[$xa (tuple-to-array t)]}
      (array-to-tuple xa 2 (array-size xa)))))
(define $T.cdr tuple-cdr) ;; alias

(define $tuple-rac
  (lambda [$t]
    (let {[$xa (tuple-to-array t)]}
      (ref xa (array-size xa)))))
(define $T.rac tuple-rac) ;; alias

(define $tuple-rdc
  (lambda [$t]
    (let {[$xa (tuple-to-array t)]}
      (array-to-tuple xa 1 (- (array-size xa) 1)))))
(define $T.rdc tuple-rdc) ;; alias

(define $tuple-cons
  (lambda [$t $ts]
    (collection-to-tuple {t @(tuple-to-collection ts)})))
(define $T.cons tuple-cons) ;; alias

(define $tuple-concat
  (lambda [$ts]
    (collection-to-tuple (concat (map tuple-to-collection (tuple-to-collection ts))))))
(define $T.concat tuple-concat) ;; alias

(define $tuple-append 2#(tuple-concat %1 %2))
(define $T.append tuple-append) ;; alias

```

解説。

+ 5〜16行目。まず、tuple を array（配列）に変換する関数 `tuple-to-array` を定義。  
前章で紹介した `read` 関数と `tuple?` 関数だけを使えば、`tuple-size` `tuple-nth` `tuple-to-collection` 等は実現可能なのですが、色々と有用なので、一旦 array に変換するようにしました。
    + Egison の `loop` マッチャーを使うことでコンパクトに記述してみました（5〜9行目）が、関数指向で 10〜16行目 のように書いた方が、パフォーマンスは良いです。
    + 9行目（14行目）で、前々節で紹介した `(read (S.concat {(show n) "#%" (show i)}))` というコードを利用しています。
+ 18〜23行目。↑を利用して `tuple-to-collection` を定義。
    + 21行目。`1#a_%1` というコードで「array（または hash）のキー `%1` に対応する要素」を参照（23行目に記述している `(lambda [$i] a_i)` と同じ意味）。またこれくらいなら、以前からある部分適用の記法である `(ref a $)` と書いてもOK。`ref` は、配列（またはハッシュ）とキーを引数に取り、その配列（またはハッシュ）の対応する値を取得する標準ライブラリ関数。
+ 28〜30行目。`tuple-to-array` の逆変換、`array-to-tuple` 関数を定義。  
引数に array だけでなく、`s`（開始インデックス）と `e`（終了インデックス）を受け取るようにすることで、部分を取り出すこともできますし、配列ではなく「キーが全て整数値のハッシュ」にも適用できるようになっています。  
純粋に配列の全要素を持つ tuple にしたければ、`(array-to-tuple a 1 (array-size a))` とすればOK。
+ 32〜34行目。`tuple-to-collection` の逆変換、`collection-to-tuple` 関数を定義。  
先ほどの `array-to-tuple` を利用していますが、渡しているのは配列ではなくハッシュです。
+ 39〜42行目。`tuple-size` 関数とそのエイリアス `T.size` を定義。`tuple-to-array` を利用しています。
    + `tuple-to-array` で array に変換してから `array-size` でサイズを取得しているので、二度手間と言えばそうなんですが、パフォーマンスの低下はほとんどありません。それは遅延評価のおかげで、`gengerate-array` expression で array を定義してもその各要素は値が必要になるまで評価されず、サイズはそれとは無関係に取得できるからです。
+ 44〜47行目。`tuple-nth` 関数とそのエイリアス `T.nth` を定義。`tuple-to-array` を利用しています。
    + `tuple-size` と同様、二度手間のようですがパフォーマンスの低下はほとんどありません。詳細略。
+ 49〜52行目。`tuple-car` 関数とそのエイリアス `T.car` を定義。collection に対する `car` を tuple に適用するもの。ただし `tuple-to-array` で変換した array に対して操作しています。
+ 54〜58行目。`tuple-cdr` 関数とそのエイリアス `T.cdr` を定義。collection に対する `cdr` を tuple に適用するもの。ただし `tuple-to-array` で変換した array に対して操作しています。
+ 60〜64行目。`tuple-rac` 関数とそのエイリアス `T.rac` を定義。collection に対する `rac` を tuple に適用するもの。以下同文略。
+ 66〜70行目。`tuple-rdc` 関数とそのエイリアス `T.rdc` を定義。collection に対する `rdc` を tuple に以下略。
+ 72〜75行目。`tuple-cons` 関数とそのエイリアス `T.cons` を定義。こちらは `tuple-to-collection` および `collection-to-tuple` を利用しています。collection に対する `cons` を tuple に以下略。
+ 77〜80行目。`tuple-concat` 関数とそのエイリアス `T.concat` を定義。こちらも `tuple-to-collection` および `collection-to-tuple` を利用し以下略。
+ 82〜83行目。`tuple-append` 関数とそのエイリアス `T.append` を定義。  
こちらは、先の `tuple-concat` を利用。「引数を2つ（の要素からなる tuple）に限定した `tuple-concat`」というワケです。

### 使用例

※ tuple_util.egi と同じディレクトリに置いて実行してください。

```clj:tuple_util_sample.egi
;; tuple_util_sample.egi

(load-file "./tuple_util.egi")

(define $t0 [])
(define $t1 [1])
(define $t2 [#t #f])
(define $t10 ["01" "02" "03" "04" "05" "06" "07" "08" "09" "10"])
(define $num 100)

(define $main
  (lambda [$args]
    (do {
      [(print (show (tuple-size t0)))]
      ; => 0
      [(print (show (tuple-size t1)))]
      ; => 1
      [(print (show (tuple-size t2)))]
      ; => 2
      [(print (show (tuple-size t10)))]
      ; => 10
      [(print (show (tuple-size num)))]
      ; => 1
      [(print (show (tuple-nth 1 t1)))]
      ; => 1
      [(print (show (tuple-nth 1 t2)))]
      ; => #t
      [(print (show (tuple-nth 2 t2)))]
      ; => #f
      [(print (show (tuple-nth 8 t10)))]
      ; => "08"
      [(print (show (tuple-to-collection t0)))]
      ; => {}
      [(print (show (tuple-to-collection t1)))]
      ; => {1}
      [(print (show (tuple-to-collection t2)))]
      ; => {#t #f}
      [(print (show (tuple-to-collection t10)))]
      ; => {"01" "02" "03" "04" "05" "06" "07" "08" "09" "10"}
      [(print (show (tuple-to-collection num)))]
      ; => {100}
      [(print (show (T.car t10)))]
      ; => "01"
      [(print (show (T.cdr t10)))]
      ; => ["02" "03" "04" "05" "06" "07" "08" "09" "10"]
      [(print (show (T.rac t10)))]
      ; => "10"
      [(print (show (T.rdc t10)))]
      ; => ["01" "02" "03" "04" "05" "06" "07" "08" "09"]
      [(print (show (T.cons "00" t10)))]
      ; => ["00" "01" "02" "03" "04" "05" "06" "07" "08" "09" "10"]
      [(print (show (T.concat [t2 t10 num])))]
      ; => [#t #f "01" "02" "03" "04" "05" "06" "07" "08" "09" "10" 100]
      [(print (show (T.concat t2 t10 num)))]
      ; => [#t #f "01" "02" "03" "04" "05" "06" "07" "08" "09" "10" 100]
      [(print (show (T.append t10 ["11" "12"])))]
      ; => ["01" "02" "03" "04" "05" "06" "07" "08" "09" "10" "11" "12"]
    })))

```

その他、ここに用意していない「collection を操作する関数」も、`tuple-to-collection` で変換し、必要なら `collection-to-tuple` で戻すことで、tuple を操作（して tuple を返す）関数が実現できます。

## 応用：可変個引数の実現

前節の例のうち、52行目 ``(T.concat [t2 t10 num])`` と、54行目 `(T.concat t2 t10 num)`。
引数を「3つの要素からなる1つの tuple」を渡しているか、「3つの tuple」を渡しているかの違いがありますが、どちらも受付同じ結果を返します。
同じ結果になる理由は、[前回の記事](http://qiita.com/antimon2/items/5184282474970c52c97d) の「1個の引数を受け取るよう定義された関数（lambda）は、複数の引数を渡すと勝手に tuple にまとめて1つの引数として扱われる」という挙動によるものです。
またその記事中に、「『可変個引数』が実現できる*可能性がある*」と書きました。

はい。実現できていますね？
54行目。今は3つの引数を渡す形になっていますが、これ、もう一つ引数を加えても、例えば `(T.concat t2 t10 num [|"A" "B" "C"|])` とすると `[#t #f "01" "02" "03" "04" "05" "06" "07" "08" "09" "10" 100 [|"A" "B" "C"|]]` という結果が得られます。ちゃんと期待通りに動きます(^-^)

もっとも、結局 `tuple-to-collection` で collection に変換することによって実現しているので、初めから collection を引数に受け取る関数を定義して、必要に応じてその引数に `tuple-to-collection` で tuple を collection に変換して渡す、とした方が効率的なコードになることは確か。
ただ、意味的に明示的に「引数をいくつでも（ただし有限個）渡せる」という関数にしたかったら、やっぱりこの方法は有用ではないかと思います。

ということで、個人的に有用だと思う例をもう1つ挙げておきます。
「引数に複数個（1個以上）の collection を受け取れる `zip`」です。
戻り値は「各要素が『引数として受け取ったそれぞれの collection の対応する要素の組からなる tuple』である collection」です（`array-to-tuple` を利用しています）。

※ tuple_util.egi と同じディレクトリに置いて実行してください。

```clj:zip_multi.egi
;; zip_multi.egi

(load-file "./tuple_util.egi")

(define $zip*
  (lambda [$t]
    (match (tuple-to-collection t) (list (list something)) {
      [<join _ <cons <nil> _>> {}]
      [(loop $i [1 $n] <cons <cons $x_i $xs_i> ...> <nil>) {(array-to-tuple x 1 n) @(zip* (array-to-tuple xs 1 n))}]})))

(define $fibs (2#{%1 @(# %2 (+ %1 %2))} 1 1))

(define $main
  (lambda [$args]
    (do {
        [((compose show print) (take 10 (zip* fibs)))]
        ; => {1 1 2 3 5 8 13 21 34 55}
        [((compose show print) (take 10 (zip* nats fibs)))]
        ; => {[1 1] [2 1] [3 2] [4 3] [5 5] [6 8] [7 13] [8 21] [9 34] [10 55]}
        [((compose show print) (take 10 (zip* nats fibs primes)))]
        ; => {[1 1 2] [2 1 3] [3 2 5] [4 3 7] [5 5 11] [6 8 13] [7 13 17] [8 21 19] [9 34 23] [10 55 29]}
        [((compose show print) (zip* nats {"A" "B" "C"}))]
        ; => {[1 "A"] [2 "B"] [3 "C"]}
        [((compose show print) (zip* {"A" "B" "C"} nats))]
        ; => {["A" 1] ["B" 2] ["C" 3]}
        [((compose show print) (zip* {1 2 3} {1 2 3} {1 2 3} {1 2 3} {1 2 3}))]
        ; => {[1 1 1 1 1] [2 2 2 2 2] [3 3 3 3 3]}
    })))

```

少しだけ解説：

+ 11行目。準備の章で定義した `fibs` を早速活用♪
+ 16行目。引数に collection を1つだけ渡した場合は、結果的に元の collection と同内容の collection になります。
+ 18行目、20行目、26行目。引数が 2つ、3つ、それ以上でも動作します。
+ 22行目、24行目。要素数が異なる場合、一番短い collection に合わせた長さになります。

## 参考

### Egison の技術情報
+ [Basics of Syntax and Semantics](http://www.egison.org/manual/basics.html)
+ [Primitive Functions](http://www.egison.org/manual/primitive-functions.html#primitives)
+ [Collection Core Library Functions](http://www.egison.org/libraries/core/collection.html)
+ [Egison Cheat Sheet](http://www.egison.org/cheatsheet.html)
+ その他 [egison.org](http://www.egison.org/) 内参照。


[^1]: Egison 3.5.3 で追加された新しい文法（Syntax Suger）等を多用したコードになっているので、3.5.2以前（3.3.x含む）では動作しません。がんばれば3.5.2以前で動くコードに変換もできますがめんどくさいです。
[^2]: https://github.com/egison/egison/releases 参照。
[^3]: Clojure 由来、だそうです。
[^4]: 日本語訳：「『1つめの引数に10をかけて2つめの引数を足す』という無名関数をその場で定義して、1 と 2 を渡す⇒12が返る」
[^5]: 日本語意訳：「『最初に与えた引数をどんどん2倍していって列挙した collection を返す関数』に 2 を渡してその最初から10個を取得⇒{2 4 8 16 32 64 128 256 512 1024}が返る」
[^6]: http://www.egison.org/manual/primitive-functions.html#primitives 参照。
[^7]: http://www.egison.org/manual/basics.html 参照。
[^8]: [前回記事の江木さんからのコメント](http://qiita.com/antimon2/items/5184282474970c52c97d#comment-e8bf2d0ea1a23e43f265) 等参照
[^9]: http://www.egison.org/manual/primitive-functions.html#primitives に、サンプルとしてこの `(tuple? [1 2])` が載っているので本来の挙動が非常に分かりづらいです。少し前までは `(tuple? 2 [1 2])` というサンプルだったような気がするのですけれど…。