Any merit to a lazy-ish juxt function?

余生颓废 提交于 2019-12-11 01:42:13

问题


In answering a question about a function that maps over multiple functions with the same arguments (A: juxt), I came up with a function that basically took the same form as juxt, but used map:

(defn could-be-lazy-juxt
  [& funs]
  (fn [& args]
    (map apply funs (repeat args))))

=> ((juxt inc dec str) 1)
[2 0 "1"]
=> ((could-be-lazy-juxt inc dec str) 1)
(2 0 "1")

=> ((juxt * / -) 6 2)
[12 3 4]
=> ((could-be-lazy-juxt * / -) 6 2)
(12 3 4)

I have little clue about the laziness or performance of it, but timing in the REPL does suggest something lazy-ish is going on.

=> (time (apply (juxt + -) (range 1 100)))
"Elapsed time: 0.097198 msecs"
[4950 -4948]
=> (time (apply (could-be-lazy-juxt + -) (range 1 100)))
"Elapsed time: 0.074558 msecs"
(4950 -4948)

=> (time (apply (juxt + -) (range 10000000)))
"Elapsed time: 1019.317913 msecs"
[49999995000000 -49999995000000]
=> (time (apply (could-be-lazy-juxt + -) (range 10000000)))
"Elapsed time: 0.070332 msecs"
(49999995000000 -49999995000000)

I'm sure this function is not really that quick (the print of the outcome 'feels' about as long in both). Doing a 'take x' on the function only limits the amount of functions evaluated, which probably is limited in it's applicability, and limiting the other parameters by 'take' should be just as lazy in normal juxt.

Is this juxt really lazy ? Would a lazy juxt bring anything useful to the table, for instance as a compositing step between other lazy functions ? What are the performance (mem / cpu / object count / compilation) implications ? Why is the Clojure juxt implemented with a reduce and returns a vector, which breaks laziness ?


回答1:


Yes your implementation of juxt is lazy by virtue of only calling map which is lazy.

it's hard to call it in it's present form with out realizing the arguments in the caller (by using apply. so I changed it a bit to take a sequence of functions:

user> (defn could-be-lazy-juxt
  [funs]
  (fn [& args]
    (map #(apply %1 %2) funs (repeat args))))
#'user/could-be-lazy-juxt

then define a lazy sequence of functions that make a lot of output when it's realized

user> (defn loud-seq [len] (take len (map #(do (println "produced a function") %) (cycle [inc dec]))))
#'user/loud-seq

then use juxt to make a function out of this lazy sequence of functions

user> (def f (could-be-lazy-juxt (loud-seq 50)))
#'user/f

as you can see the list is still lazy, it realizes it's list of functions when it's resulting function is called.

so lets call it:

user> (f 1)
(produced a function
produced a function
2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 produced a function
produced a function
0 2 0)
user> 

I leave the reasons for doing so up to you ;)



来源:https://stackoverflow.com/questions/10049925/any-merit-to-a-lazy-ish-juxt-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!