2012-06-17 7 views

答えて

14

このプログラミングスタイルはCLでは原則として可能ですが、Lisp-2の場合、#'funcallをいくつか追加する必要があります。また、例えばHaskellとは対照的に、関数は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))) 

(それらは私が手早くだけ迅速例です - 彼らは本当にテストまたは良く考えスルー異なるユースケースのためにされていません。これら付)

、Haskellではmap ((*2) . (+1)) xsのようなものは次のようになります。

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のように組み込まれています。チー/シェンで

は、関数はカリー化されており、暗黙の部分のアプリケーションは、(関数は一つの引数で呼び出されたときに)サポートされています:

(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 
2

はい、これは一般的に正しい機能で可能です。例えば、ここではWikipediaのページからsumを実装ラケットの例である:

#lang racket 
(define sum (curry foldr + 0)) 

手順は、デフォルトではカリー化されていないので、それはcurryを使用するか、または明示的カリースタイルであなたの関数を作成するのに役立ちます。 curryingを使用する新しいdefineマクロでこれを抽象化することができます。

+2

質問はCommon Lispに関するものですが、この回答はSchemeには正しいですが、CLについては正しくありません –

+1

まあ、実際は質問は一般にLispに関するものでした。私はタグとしてCLを追加しました。なぜなら、それは私が最もよく知っているLisp方言ですが、Schemeを使ったこの答えは同様に便利です。 – paldepind

3

はい、@ダンリーイはすでに説明しました。 Paul Grahamの第6章でANSI Common Lispという本のいくつかの例を追加します。関数ビルダーで6:

あなたはこのような関数ビルダーを定義することができます。

(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)))) 

にこれはコンええ、任意の数の引数を取ることができることを意味equlvalentです。

引数に3を追加する機能行います

(curry #'+ 3) 

は本の中でより多くを参照してください。

+2

しかしそれほど意味をなさない。それは悪いコードにつながります。読み込みとデバッグが難しい。プラス - ほとんどすべてのCLコンパイラは、そのための遅いコードを生成します(cons argリストなど)。 –

+0

@RainerJoswigあなたはそうです。これは、どのように柔軟なCLであるか、どのようなクロージャが私たちを助けるかもしれないかを示すことができます... o_O – juanitofatas

+2

あなたの 'compose'の例も私のバージョンで動作します。私はちょうど今修正された 'comp'(' step'の関数呼び出しの間違った順序、REPLの作業版)に小さなコピー&ペーストエラーやタイプミスがありました。また、Rainerが正しいと思う:CLでこれを行うことができるのは良いことだが、あまり慣れない。ハスケルは、このプログラミングスタイルにもっと役立ちます。(グラハムのスタイル、btwは、多くのCLプログラマーによってかなり独特のものと考えられています。) – danlei

7

次のようなものを使用することができます(これはないですもう少し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を呼びましょう - )とあなたが->を使用する予定のパッケージなので、->$が継承されるのuse句で tacitを入れ

使い方のの

例:

(-> "TEST" 
    string-downcase 
    reverse) 

(-> "TEST" 
    reverse 
    (elt $ 1)) 

これは、より多くのF#の|>(およびシェルパイプ)のようなHaskellの.よりもですが、彼らは は私が|>を好む(ほとんど同じことですが、これは問題です個人的な好み)。

->が何をしているかを確認するには、わずか3回(SLIMEで、これは一例では最初(にカーソルを置くとC-c RET 3回入力することによって達成される)最後の例をマクロ展開しました。