Lispで暗黙のプログラミング(ポイントフリープログラミングとも呼ばれる)を使用/実装することは可能ですか? 答えが「はい」の場合、それは実行されていますか?
4 に答える
このスタイルのプログラミングは、原則として CL で可能ですが、Lisp-2 であるため、いくつか#'
の とを追加する必要がありfuncall
ます。また、たとえば Haskell とは対照的に、関数は CL ではカリー化されておらず、暗黙的な部分適用はありません。一般に、そのようなスタイルはあまり慣用的な CL ではないと思います。
たとえば、部分適用と構成を次のように定義できます。
(defun partial (function &rest args)
(lambda (&rest args2) (apply function (append args args2))))
(defun comp (&rest functions)
(flet ((step (f g) (lambda (x) (funcall f (funcall g x)))))
(reduce #'step functions :initial-value #'identity)))
(これらは私が思いついた簡単な例です。さまざまなユースケースについて実際にテストしたり、よく考えたりしたわけではありません。)
map ((*2) . (+1)) xs
これらにより、Haskellのようなものは次のようになります。
CL-USER> (mapcar (comp (partial #'* 2) #'1+) '(1 2 3))
(4 6 8)
sum
例:
CL-USER> (defparameter *sum* (partial #'reduce #'+))
*SUM*
CL-USER> (funcall *sum* '(1 2 3))
6
(この例では、funcall を回避するために、関数を値セルに格納する代わりに、シンボルの関数セルを設定することもできます。)
ちなみにEmacs Lispでは、部分適用は として組み込まれていapply-partially
ます。
Qi/Shen では、関数はカリー化され、暗黙的な部分適用 (関数が 1 つの引数で呼び出される場合) がサポートされます。
(41-) (define comp F G -> (/. X (F (G X))))
comp
(42-) ((comp (* 2) (+ 1)) 1)
4
(43-) (map (comp (* 2) (+ 1)) [1 2 3])
[4 6 8]
Clojure には、「パイプライン処理」と同様の感覚を与えるシンタックス スレッド シュガーもあります。
user=> (-> 0 inc (* 2))
2
->
次のようなものを使用できます(これはClojureよりも少し多くなります):
(defmacro -> (obj &rest forms)
"Similar to the -> macro from clojure, but with a tweak: if there is
a $ symbol somewhere in the form, the object is not added as the
first argument to the form, but instead replaces the $ symbol."
(if forms
(if (consp (car forms))
(let* ((first-form (first forms))
(other-forms (rest forms))
(pos (position '$ first-form)))
(if pos
`(-> ,(append (subseq first-form 0 pos)
(list obj)
(subseq first-form (1+ pos)))
,@other-forms)
`(-> ,(list* (first first-form) obj (rest first-form))
,@other-forms)))
`(-> ,(list (car forms) obj)
,@(cdr forms)))
obj))
$
(配置するパッケージからシンボルをエクスポートすることにも注意する必要があります-->
そのパッケージを呼び出しましょう-使用する予定のパッケージの句にtacit
入れる
ので、継承されます)tacit
use
->
->
$
使用例:
(-> "TEST"
string-downcase
reverse)
(-> "TEST"
reverse
(elt $ 1))
|>
これは、HaskellよりもF# (およびシェルパイプ).
に似ていますが、ほとんど同じです(私は好み|>
ますが、これは個人的な好みの問題です)。
何->
が行われているのかを確認するには、最後の例を3回マクロ展開します(SLIMEでは、これは、例の最初(
の例にカーソルを置き、3回入力することで実行されますC-c RET
)。
はい、それは可能であり、@danleiはすでに非常によく説明しています。関数ビルダーに関する第6.6章のPaulGrahamによる本ANSICommonLispからいくつかの例を追加します。
次のように関数ビルダーを定義できます。
(defun compose (&rest fns)
(destructuring-bind (fn1 . rest) (reverse fns)
#'(lambda (&rest args)
(reduce #'(lambda (v f) (funcall f v))
rest
:initial-value (apply fn1 args)))))
(defun curry (fn &rest args)
#'(lambda (&rest args2)
(apply fn (append args args2))))
このように使用します
(mapcar (compose #'list #'round #'sqrt)
'(4 9 16 25))
戻り値
((2) (3) (4) (5))
compose
関数呼び出し:
(compose #'a #'b #'c)
と同等です
#'(lambda (&rest args) (a (b (apply #'c args))))
つまり、composeは任意の数の引数を取ることができます。
引数に3を追加する関数を作成します。
(curry #'+ 3)
本でもっと見てください。
はい、これは一般的に適切な機能で可能です。たとえばsum
、ウィキペディアのページからのRacket 実装の例を次に示します。
#lang racket
(define sum (curry foldr + 0))
プロシージャはデフォルトではカリー化されていないためcurry
、明示的にカリー化されたスタイルで関数を使用または記述すると役立ちます。define
カリー化を使用する新しいマクロを使用して、これを抽象化できます。