2011-11-18 31 views
12

Mỗi lần như vậy, tôi thường muốn áp dụng một tập hợp các hàm trên một số tập hợp tham số. Thật dễ dàng để làm với bản đồ và một chức năng rất đơn giản.Phiên bản tiêu chuẩn hoặc sử dụng thành ngữ (fn [f & args] (áp dụng f args))

(map 
    (fn invoke [f & args] (apply f args)) 
    [- + *] 
    [1 2 3] 
    [1 2 3] 
    [1 2 3]) 

(-1 6 27) 

Tìm kiếm trên web có một vài thư viện xác định chức năng tương tự, thường được gọi là funcall hoặc gọi. Bởi vì thiên hướng của Clojure cho các hàm variadic, tôi không thể không nghĩ rằng nên có một phiên bản mặc định của hàm này.

Có hay không có cách nào khác để giải quyết các tình huống như thế này?

Edit:

hình thức khác có thể là

(map 
    (comp eval list) 
    [- + *] 
    [1 2 3] 
    [1 2 3] 
    [1 2 3]) 

(-1 6 27) 

nào làm tôi sợ vì eval.

Trả lời

6

Không có chức năng funcall hoặc tương đương trong thư viện Clojure chuẩn hoạt động chính xác theo cách này. "áp dụng" là khá gần nhưng cần một bộ sưu tập các đối số ở cuối chứ không phải thuần túy là variadic.

Với điều này trong tâm trí, bạn có thể "ăn gian" với áp dụng để làm cho nó hoạt động như sau bằng cách thêm một danh sách vô hạn của Nils đến cùng (mà được coi là chuỗi rỗng của các đối số bổ sung):

(map apply [- + *] [1 2 3] [1 2 3] [1 2 3] (repeat nil)) 
=> (-1 6 27) 

Nhìn chung, tôi nghĩ rằng cách tiếp cận hợp lý nếu bạn thực sự muốn sử dụng chức năng này thường xuyên chỉ là để xác định nó:

(defn funcall [f & ps] 
    (apply f ps)) 

(map funcall [- + *] [1 2 3] [1 2 3] [1 2 3]) 
=> (-1 6 27) 
+0

Vì vậy, những gì bạn nói là nếu tham số áp dụng cuối cùng là một số không, nó bằng cách nào đó được hiểu là một chuỗi? (áp dụng + 1 2 3 nil) không hoạt động. Khá thú vị, tôi hy vọng thats cố ý mặc dù bởi vì nó có thể đến trong RẤT tiện dụng. – NielsK

+1

Có - nil được coi là một chuỗi rỗng khá nhiều trong suốt Clojure. ví dụ. (vec nil) => [] hoặc (seq []) => nil. Bạn có thể dựa vào hành vi này. – mikera

7

Edit: này sẽ làm những gì bạn muốn (như @BrandonH đề cập):

(map #(apply %1 %&) [- + *] [1 2 3] [1 2 3] [1 2 3]) 

Nhưng điều này là hầu như không một sự cải tiến so với phiên bản của bạn - nó chỉ sử dụng một cách viết tắt cho các chức năng ẩn danh.


sự hiểu biết của tôi là FUNCALL là cần thiết trong Common Lisp, vì nó là một Lisp-2, trong khi Clojure là một Lisp-1.

+1

Kết quả của chức năng của tôi là (-1 6 27), của bạn là ([-1 3 1] [-2 6 8] [-3 9 27]). Đã chỉnh sửa câu hỏi của tôi nên ý nghĩa phải rõ ràng hơn. – NielsK

+0

@NielsK - crap, tôi không có quyền truy cập Clojure vào lúc này. Tôi sẽ phải chờ đợi để tìm ra những gì tôi hơi say. –

+0

Không có vấn đề gì, đã suy nghĩ theo những dòng đó (juxt), nhưng bằng cách nào đó kết thúc với giải pháp gọi là đơn giản nhất. – NielsK

0

Hiện tại, tôi không thể sử dụng chức năng clojure.core mà bạn có thể cắm vào bản đồ của mình và thực hiện theo những gì bạn muốn. Vì vậy, tôi muốn nói, chỉ cần sử dụng phiên bản của riêng bạn.

Matt có lẽ là đúng rằng lý do không có một funcall, là bạn hầu như không bao giờ cần đến nó trong một Lisp-1 (có nghĩa là, chức năng và các ràng buộc khác chia sẻ không gian cùng tên trong clojure)

2
(map #(%1 %2 %3 %4) [- + *][1 2 3][1 2 3][1 2 3]) 

(-1 6 27) 

Vấn đề là nếu bạn muốn cho phép một số biến đối số, cú pháp & đặt các giá trị trong vec-tơ, yêu cầu sử dụng áp dụng. Giải pháp của bạn có vẻ ổn với tôi nhưng như Brandon H đã chỉ ra, bạn có thể rút ngắn giải pháp đó thành #(apply %1 %&).

Là người trả lời khác đã lưu ý, nó không có gì để làm với funcall mà tôi nghĩ được sử dụng trong Lisps khác để tránh sự nhập nhằng giữa các ký hiệu và chức năng (lưu ý rằng tôi gọi là chức năng như (%1 ...) ở đây, không (funcall %1 ...).

+1

(bản đồ # (áp dụng% 1% &) [- + *] [1 2 3] [1 2 3] [1 2 3]) hoàn toàn có thể chấp nhận được theo ý kiến ​​của tôi. –

+1

Tôi đồng ý, nhưng về cơ bản giống với phiên bản người gửi. –

10

Nếu bạn thực sự không có một đầu mối về tên hàm, nhưng bạn biết những gì trong và đầu ra phải được, bạn có thể thử https://github.com/Raynes/findfn.

(find-arg [-1 6 27] map '% [- + *] [1 2 3] [1 2 3] [1 2 3]) 
;=> (clojure.core/trampoline) 

này cho chúng ta biết

(map trampoline [- + *] [1 2 3] [1 2 3] [1 2 3]) 
;=> (-1 6 27) 

Thực ra, bạn có thể lạm dụng tấm bạt lò xo như funcall trong clojure. Nhưng nó hầu như không thành ngữ, bởi vì nó là một Lisp-1.Mã trên đánh giá đến:

[(trampoline - 1 1 1), (trampoline + 2 2 2), (trampoline * 3 3 3)] mà sau đó trở thành [-1 6 27] (dưới hình thức của lazyseq chính xác).

Như Adrian Mouat chỉ ra trong bình luận dưới đây, điều này có lẽ không phải là cách ưu tiên để giải quyết nó. Sử dụng một funcall như xây dựng có mùi hơi buồn cười. Phải có một giải pháp sạch hơn. Cho đến khi bạn thấy rằng, findfn có thể hữu ích ;-).

+2

Tôi đã bình chọn là một câu trả lời rất thú vị, nhưng tôi sẽ không tư vấn giải quyết nó theo cách này! –

+0

Tôi đồng ý, đó là lý do tại sao phần đầu tiên của câu trả lời là: "đây là những gì juxt là" ... –

+1

Vẫn không thuyết phục về phần juxt, Matt (gỡ bỏ trong chỉnh sửa) câu trả lời đã cho kết quả sai. (bản đồ (áp dụng juxt [- + *]) [1 2 3] [1 2 3] [1 2 3]) cho kết quả ([-1 3 1] [-2 6 8] [-3 9 27]) thay vào đó trong số (-1 6 27). Có vẻ như findfn có thể khá hữu ích! – NielsK

2

Cá nhân tôi nghĩ phiên bản đầu tiên của bạn khá rõ ràng và thành ngữ.

Dưới đây là một sự thay thế, bạn có thể tìm thấy thú vị để xem xét tuy nhiên:

(map 
    apply 
    [- + *] 
    (map vector [1 2 3] [1 2 3] [1 2 3])) 

=> (-1 6 27) 

Lưu ý lừa của việc sử dụng (vector bản đồ ....) để transpose chuỗi các đối số vào ([1 1 1] [2 2 2] [3 3 3]) để chúng có thể được sử dụng trong hàm áp dụng.

0

Điều gì về điều này? Nó chọn các giá trị trả về có liên quan từ juxt. Vì đây là tất cả lười biếng, nó chỉ nên tính toán các yếu tố cần thiết.

user> (defn my-juxt [fns & colls] 
     (map-indexed (fn [i e] (e i)) 
      (apply map (apply juxt fns) colls))) 
#'user/my-juxt 
user> (my-juxt [- + *] [1 2 3] [1 2 3] [1 2 3]) 
(-1 6 27) 
+0

Vâng, nó sử dụng juxt, nó hoạt động, nhưng tôi nghi ngờ nó ngắn gọn hơn hoặc thành ngữ :) – NielsK

1

Một cách tiếp cận đó là khá tự giải thích: "đối với từng chức năng thứ n, áp dụng nó cho tất cả các yếu tố thứ n của các vectơ":

(defn my-juxt [fun-vec & val-vecs] 
    (for [n (range 0 (count fun-vec))] 
    (apply (fun-vec n) (map #(nth % n) val-vecs)))) 

user> (my-juxt [- + *] [1 2 3] [1 2 3] [1 2 3]) 
(-1 6 27) 
Các vấn đề liên quan