Hy (Hylang) は Python で動作する Lisp 方言のひとつです。プログラムを S 式 (リスト) で記述するところは Lisp (Clojure とよく似ている) なのですが、データ構造は Python のものを使うことを前提としているようで、Lisp の cons, car, cdr に相当する関数がないところがユニークです。そのかわり、Python の機能 (ライブラリ) は簡単に利用することができますし、Common Lisp ライクなマクロも用意されています。
Hy は pip で簡単にインストールすることができます。
python3 -m pip install hy
Hy を実行するときはシェルで hy と入力してください。
$ hy Hy 0.28.0 using CPython(main) 3.10.12 on Linux =>
メッセージとプロンプト '=>' が表示され、対話モードで Hy が起動されます。終了するときは (quit) または CTRL-D を入力してください。
簡単な実行例を示しましょう。
=> (print "hello, world") hello, world => (+ 1 2 3) 6 => (setv a [1 2 3 4 5]) => a [1 2 3 4 5] => (.append a 6) => a [1 2 3 4 5 6] => (.pop a) 6 => a [1 2 3 4 5] => (a.append 6) => a [1 2 3 4 5 6] => (a.pop) 6 => a [1 2 3 4 5] => (get a 0) 1 => (get a 4) 5 => (setv (get a 2) 10) => a [1 2 10 4 5]
関数呼び出しは Lisp と同じく (関数 引数 ...) になります。変数の代入は setq や setf ではなく setv を使います。Hy の場合、角カッコ [] は Python のリスト (1 次元配列) に変換されます。要素はカンマではなく空白で区切ってください。
Python のメソッドは obj.method(args, ...) で呼び出しますが、Hy では (.method obj args ...) という形式になります。Hy ver 0.28 では、(obj.method args ...) でも呼び出すことができるようです。リストの要素は get で読み込み、setv と get で書き込みを行います。これは Common Lisp の aref, setf と同じですね。
=> (defn fact [n] (if (= n 0) 1 (* n (fact (- n 1))))) => (fact 9) 362880 => (fact 10) 3628800 => (fact 11) 39916800 => (for [x (range 10)] (print (fact x))) 1 1 2 6 24 120 720 5040 40320 362880
関数定義は defn で、引数は [ ] で囲みます。for は Python の for 文と同じです。
(for [var iterable] body ...)
range は Python の range と同じで iterable を返します。Python と同様に iterable は関数 list で Python のリストに変換できます。
=> (range 10) (range 10) => (list (range 10)) [0 1 2 3 4 5 6 7 8 9] => (list (range 0 10)) [0 1 2 3 4 5 6 7 8 9] => (list (range 0 10 2)) [0 2 4 6 8]
なお、リストの生成には内包表記を使ったほうが簡単です。Hy では lfor を使います。
(lfor var iterable body)
=> (lfor x (range 10) x) [0 1 2 3 4 5 6 7 8 9] => (lfor x (range 10) (* x x)) [0 1 4 9 16 25 36 49 64 81] => (lfor x (range 3) y (range 3) [x y]) [[0 0] [0 1] [0 2] [1 0] [1 1] [1 2] [2 0] [2 1] [2 2]] => (lfor x (range 10) :if (= (% x 2) 0) x) [0 2 4 6 8]
条件式は :if で指定します。詳細は Hy のドキュメントをお読みくださいませ。
モジュール (ライブラリ) の読み込みは import で行います。
=> (import math) => (math.sqrt 2) 1.4142135623730951 => (import fractions [Fraction]) => (setv x (Fraction 1 2)) => x (Fraction 1 2) => (setv y (Fraction 1 3)) => y (Fraction 1 3) => (+ x y) (Fraction 5 6) => (- x y) (Fraction 1 6) => (* x y) (Fraction 1 6) => (/ x y) (Fraction 3 2)
(import module) は Python の import module と同じ、(import module [name]) は from module import name と同じです。fractions は分数 (有理数) を扱うモジュールで、Fraction は分数を表すクラスです。fractions を import することで、Hy でも分数の計算を行うことができます。
このように、見た目は Lisp ライクな Hy ですが、その中身は Python 寄りのように感じました。Lisp は List Processor の略で、連結リストを操作するのが得意な言語です。Hy で「リスト遊び」をするのは、Lisp ほど簡単ではなさそうですが、気軽に Python の機能を利用できるのは便利ですね。興味のある方はいろいろ試してみてください。
数学の世界では、二次方程式 \(x^2 - n x - 1 = 0\) の正の解を「貴金属数 (metallic number)」といいます。
n 番目の貴金属数を「第 n 貴金属数」といいます。一番有名なのは第 1 貴金属数で、「黄金数 (golden number)」といいます。このほかに、第 2 貴金属数の「白銀数 (silver number)」や第 3 貴金属数の「青銅数 (bronze number)」などがあります。
次の漸化式で生成する数列において、隣り合う二項の比は第 n 貴金属数に収束します。
n = 1 とすると「フィボナッチ数列」になりますね。つまり、フィボナッチ数列の隣接する二項の比は黄金数に収束します。n = 2 で生成される数列の数を「ペル数 (Pell number)」といい、隣り合う二項の比は白銀数に収束します。n = 3 であれば青銅数に収束します。
漸化式をプログラムするのは簡単です。Python で試してみました。
>>> def metallic_number(k, n, a = 0, b = 1): ... if k == 0: return a ... return metallic_number(k - 1, n, n * a + b, a) ... >>> for n in range(1, 9): ... for k in range(16): print(metallic_number(k, n), end=" ") ... print("") ... 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 0 1 2 5 12 29 70 169 408 985 2378 5741 13860 33461 80782 195025 0 1 3 10 33 109 360 1189 3927 12970 42837 141481 467280 1543321 5097243 16835050 0 1 4 17 72 305 1292 5473 23184 98209 416020 1762289 7465176 31622993 133957148 567451585 0 1 5 26 135 701 3640 18901 98145 509626 2646275 13741001 71351280 370497401 1923838285 9989688826 0 1 6 37 228 1405 8658 53353 328776 2026009 12484830 76934989 474094764 2921503573 18003116202 110940200785 0 1 7 50 357 2549 18200 129949 927843 6624850 47301793 337737401 2411463600 17217982601 122937341807 877779375250 0 1 8 65 528 4289 34840 283009 2298912 18674305 151693352 1232221121 10009462320 81307919681 660472819768 5365090477825 >>> for k in range(1, 10): print(metallic_number(k + 1, 1) / metallic_number(k, 1)) ... 1.0 2.0 1.5 1.6666666666666667 1.6 1.625 1.6153846153846154 1.619047619047619 1.6176470588235294 >>> for k in range(1, 10): print(metallic_number(k + 1, 2) / metallic_number(k, 2)) ... 2.0 2.5 2.4 2.4166666666666665 2.413793103448276 2.414285714285714 2.4142011834319526 2.4142156862745097 2.414213197969543 >>> for k in range(1, 10): print(metallic_number(k + 1, 3) / metallic_number(k, 3)) ... 3.0 3.3333333333333335 3.3 3.303030303030303 3.302752293577982 3.3027777777777776 3.302775441547519 3.3027756557168324 3.302775636083269
貴金属数の一般項はフィボナッチ数と同様に求めることができます。興味のある方は拙作のページ Memorandum (数学編) 初等整数論 [5] をお読みくださいませ。