2010-07-06 19 views
11

Tümü, Clojure diline bir göz atmaya başladım ve yapmaya çalıştığım bir şey hakkında birkaç soru sordum. Geniş amaç, every? dizi fonksiyonunu all?'a taklit etmektir. Eminim, takma isim yapan bir işlev ya da makro (ya da bu satırlardaki bir şey) var, ama şimdiye kadar bildiğim temel yapıların bir kısmının mümkün olup olmadığını görmek istedim. Benim yaklaşımım, argümanlarını every? uygulamasına uygulayan all? adlı bir işlevi tanımlamak olacaktı.Clojure Metagramgram Oluşturma (yeni başlayanlar için!)

Bunun agnostik yapılıp yapılmayacağını merak ediyorum, bu yüzden iki argüman, yeni ad (bir Anahtar Kelime olarak) ve eski ad (bir işlev referansı olarak) almak için takma ad işlevimi parametrelemek istedim. Bu hedefe ulaşmak için iki sorunla karşılaştım.

1) Anahtar sözcüklerle adlandırılmış işlevlerin tanımlanması hataları atar. Görünüşe göre clojure.lang.IObj istiyor.

user=> (defn :foo "bar")  
java.lang.ClassCastException: clojure.lang.Keyword cannot be cast to clojure.lang.IObj (NO_SOURCE_FILE:0) 

bir mesafede bir değer ile yeni tanımlanan fonksiyonun adı parameterize bir IObj veya başka yollarla bir Anahtar döküm için bir işlevi var mı? tek bir değişken bir işleve tüm bağımsız değişkenler toplamak)

irb(main)> self.class.instance_eval do 
irb(main)* define_method(:foo) { "bar" } 
irb(main)> end 
=> #<Proc> 
irb(main)> foo 
=> "bar" 

2 (Ruby, diğer teknikler arasında define_method yapar). (+ 1 2 3 4) gibi temel işlevler bile değişken miktarda argüman alır. Şimdiye kadar gördüğüm tüm fonksiyon tanımlama teknikleri, işlev gövdesinde işlem yapmak için her şeyi bir liste halinde toplamanın hiçbir yolu olmadan, belirli bir miktarda argüman alır. Bir kez daha, Ruby'ye şu şekilde devam ediyorum:

irb(main)> def foo(*args) 
irb(main)> p args 
irb(main)> end 
=> nil 
irb(main)> foo(1, 2, 3) 
[1, 2, 3] 
=> nil 

Bana sağlayabileceğiniz herhangi bir yardım için teşekkürler!

cevap

17

Ben, kurşun noktalarında cevap olacak bakın. örtük takip etmek ne bulunur, ama belki de kendi başına bir mermi garanti hangi

  • şey: (defn tarafından ve özellikle) üst düzey def & Co tarafından oluşturulan nesneler Vars vardır. Yani aslında yapmak istediğiniz şey Var; işlevler, gerçekten isimleri olmayan düzenli değerlerdir (bedenlerinde kendi içlerine kendilerine bağlı bir isim olsalar da, bu konuyla ilgisi olmayan bir şey olsa da).

  • mevcut bir "aliasing makro" Clojure gerçekten de var - clojure.contrib.def/defalias:

    (use '[clojure.contrib.def :only [defalias]]) 
    (defalias foo bar) 
    ; => foo can now be used in place of bar 
    

    (def foo bar) üzerinden bu avantajı olduğunu kopyalar meta veri üzerinde (örneğin docstringe gibi); Daha önceki sürümlerde bunu engelleyen bir hatayı hatırlamasa da, geçerli KAFA'da makrolarla çalıştığı görülüyor.

  • Varlar anahtar kelimeler değil sembollerle adlandırılır.Clojure'deki (ve diğer Lisp'lerdeki) sembol hazırlıkları, sütunlarla başlamıyor (:foo, bir sembol değil, bir anahtar sözcüktür). Böylece

    (defn foo [...] ...) 
    
  • defn yazmalısınız foo adlı bir işlev tanımlamak oluşturulmasını kolaylaştıran bir yardımcı makro olduğunu yeni fonksiyon tutma programcı def & fn sözdizimi bir karışımını kullanmak için izin vererek Vars. Bu nedenle, defn, diğer adlar oluşturmak için gerekli olan, önceden var olan değerlere sahip Varlar oluşturmaya yönelik (söz konusu işlevler olabilir) söz konusu değildir; Bunun yerine defalias veya def kullanın.

  • kullanımı, aşağıdaki sözdizimini variadic işlevi oluşturmak için:

    (fn [x y & args] ...) 
    

    x ve y konumsal parametreleri gerekli olacaktır; işleve iletilen argümanların geri kalanı (herhangi bir sayı) bir sıraya toplanır ve args adı altında kullanılabilir. Gerekmiyorsa "gerekli konum argümanları" belirtmeniz gerekmez: (fn [& args] ...).

    , bir Var variadic devreye sokar oluşturmak yukarıdaki örneklerde ya da belki de args seq gibi bir seqable nesnesine monte var ettik bazı argümanlar (bir fonksiyon uygulamak için

    (defn foo [x y & args] ...) 
    
  • kullanmak için bir vektör & c) apply kullanın: takma ad oluşturmak için işlevi yazmak istiyorsanız

    (defn all? [& args] 
        (apply every? args)) 
    
  • - aksine. Bir makro - işlevin semboller veya Varlar kabul edilip edilmeyeceğine bağlı olarak intern, with-meta, meta - ve muhtemelen resolve/ns-resolve işlevlerini araştırmanız gerekir. Okuyucuya bir alıştırma olarak detaylarda doldurmayı bırakacağım. :-)

+1

Sadece son paragrafta ima edilen tasarımı gerçekleştiren 'intern-alias' işlevine sahip bir Gist yayınladı. Bir saniyede bir docstring ekleyeceğim. Bu meta, orijinalden takma adaya kopyalar, mevcut adın dışındaki ad alanlarındaki takma adlar oluşturmayı destekler, her iki sembolü de kabul eder ve Vars aslı olarak kabul eder & c. Bkz. Http://gist.github.com/464970 –

+0

Hadi, ahbap. En azından başkalarına bir şans ver! Zeka seviyesini * normal * 'e düşürmek için alabileceğiniz bir çeşit ilaç yok mu? Bize geri dön kardeşim, seni özlüyoruz. : p – Rayne

+0

Derin yorum için teşekkürler. Kesinlikle aradığım şeylerin çoğunu aydınlattı. # 3 numaralı noktaya ilişkin olarak (vars, anahtar kelimeler değil sembollerle adlandırılır), bir anahtar kelimeyi sembol haline getirmenin bir yolu var mıdır? Diyelim ki, bir egzersiz yapmak için, def'i sarmış bir fonksiyon yazmak istedim ve iki argüman, bir anahtar kelime ve bir değer aldım ve daha sonra dahili olarak def denir ve değişkene anahtar kelimenin ismini koydu. Mümkün? –

6

Tek yapmanız gereken her şeyi bağlamak mı? herkese fonksiyon? def yoluyla yapılır sembol, bu konuyu daha biraz için

(def all? every?) 

, sorular ayrı bir takım konularda içine düzgünce bölünebilir beri Clojure macro to create a synonym for a function

+0

ben böyle bir özelliğin uygulanmasını araştırıyordu, ancak bunu yapmanın hızlı yolunu da biliyorum, teşekkürler! –

3

belki argüman koleksiyonu ve yıkım üzerine Yakut gezgin sözlükte boşlukları bir çift doldurun dışında burada mevcut açıklamalar çok ekleyebilirsiniz sanma:

(defn foo [& args]     ; Ruby: def foo(*args) 
    (println args))  
user=> (foo 1 2 3) 
(1 2 3) 

(defn foo [& args] 
    (+ args)) 
user=> (foo 1 2 3) 
java.lang.ClassCastException  ; + takes numbers, not a list 

(defn foo [& args] 
    (apply + args))     ; apply: as Ruby proc.call(*args) 
user=> (foo 1 2 3) 
6 

(defn foo [& args] 
    (let [[a b & other] args]  ; Ruby: a, b, *other = args 
    (println a b other))) 
user=> (foo 1 2 3) 
1 2 (3) 
+0

Eş anlamlılar için teşekkürler. :] –

İlgili konular