Clojure macro to create a synonym for a function

后端 未结 2 476
后悔当初
后悔当初 2021-01-18 09:57

Probably an easy one for anyone who actually knows how to write macros in any Lisp. I want to be able to define synonyms for function names. I\'ve been copy-and-paste hack

相关标签:
2条回答
  • 2021-01-18 10:08

    If I understand your question, there's an easier way: def the new symbol to the old function.

    user=> (def foo +)
    #'user/foo 
    user=> (foo 1 2) 
    3
    

    The performance of def also outperforms the macro approach:

    (defmacro foo2 [& args]
      `(+ ~@args))
    

    foo2 is then effectively an alias for + and behaves exactly the same way (being rewritten as +) except for the restrictions that are placed on using macros where a value must be returned.

    If you want the behavior of the "alias" to be exactly the same as that of the original function (callable in the same contexts as well) then you need to use def to rename the function.

    user=> (def foo +)
    
    user=> (defn foo1 [& args]
             `(+ ~@args))
    
    user=> (defmacro foo2 [& args]
             `(+ ~@args))
    
    user=> (time (dotimes [n 1000000] (foo 1 n)))
    "Elapsed time: 37.317 msecs"
    
    user=> (time (dotimes [n 1000000] (foo1 1 n)))
    "Elapsed time: 292.767 msecs"
    
    user=> (time (dotimes [n 1000000] (foo2 1 n)))
    "Elapsed time: 46.921 msecs"
    
    0 讨论(0)
  • 2021-01-18 10:21

    Macros are faster now

    I have embarked on the (most assuredly foolish) task of renaming some of Clojure's core functions in one of my projects. I've been having great fun with it (fn becomes λ, loop becomes , etc.), but I found myself very curious about performance. Greg's excellent answer from five years ago is now, I think, partially wrong. I'm using Clojure 1.5.1.

    For starters:

    user=> (defn foo1 [& args] `(+ ~@args))
    #'user/foo1
    user=> (foo1 1 2 3 4)
    (clojure.core/+ 1 2 3 4)
    

    That's definitely not what you want. Moreover, it appears that now macros are definitely the fastest option. I duplicated Greg's experiments and got very different results. The times you see below are each the best of ten runs:

    user=> (def foo +)
    #'user/foo
    user=> (defn foo1 [& args] (apply + args))
    #'user/foo1
    user=> (defmacro foo2 [& args] `(+ ~@args))
    #'user/foo2
    user=> (time (dotimes [n 1000000] (+ 1 n)))
    "Elapsed time: 53.401812 msecs"
    user=> (time (dotimes [n 1000000] (foo 1 n)))
    "Elapsed time: 135.675486 msecs"
    user=> (time (dotimes [n 1000000] (foo1 1 n)))
    "Elapsed time: 494.770352 msecs"
    user=> (time (dotimes [n 1000000] (foo2 1 n)))
    "Elapsed time: 53.509264 msecs"
    

    Also, I think the difference between the methods becomes insignificant as the function does more. This is the experiment I had run originally in which I found no difference between them:

    user=> (defmacro α [& body] `(reduce ~@body))
    #'user/α
    user=> (defn β [& body] (apply reduce body))
    #'user/β
    user=> (def γ reduce)
    #'user/γ
    user=> (time (dotimes [n 10000] (reduce + (range n))))
    "Elapsed time: 5466.920266 msecs"
    user=> (time (dotimes [n 10000] (α + (range n))))
    "Elapsed time: 5474.532622 msecs"
    user=> (time (dotimes [n 10000] (β + (range n))))
    "Elapsed time: 5491.337517 msecs"
    user=> (time (dotimes [n 10000] (γ + (range n))))
    "Elapsed time: 5456.271967 msecs"
    

    Finally, what you're looking for might be defalias from clojure.contrib.def. I have no experience with it.

    0 讨论(0)
提交回复
热议问题