我非常喜欢这个问题。也许我很惊讶看到一个如此简单的新问题。无论如何,流口水,我认为我有一些适合你的东西。
而不是做:
(if (or @future1 @future2)
...)
做:
(if (or (and (realized? future1) @future1)
(and (realized? future2) @future2))
...)
诀窍是在询问是true 还是false 之前测试是否已实现。
这可以概括为这样的宏:
(defmacro future-or [& futures]
`(or ~@(for [f futures]
`(and (realized? ~f)
(deref ~f)))))
然后像这样使用:
(if (future-or future1 future2)
...)
等一下。也许我不正确地理解你的问题。也许您想阻止执行,直到其中一个期货完成并返回 true,在这种情况下,您执行 if 的 then 子句,或者您的两个期货都完成并且都不返回 true,其中如果您执行 if 的 else 子句。那是另一回事。
我能想出的最简洁的方法不是很漂亮,但也不是很长:
(if (loop []
(cond (or (and (realized? future1) @future1)
(and (realized? future2) @future2)) true
(and (realized? future1) (realized? future2)
(not @future1) (not @future2)) false
:else (recur)))
...)
现在,这使用loop 重复循环,直到发生以下两种情况之一:任一期货都已实现和true,在这种情况下,循环返回true;或者所有期货都已实现并且全部为false,在这种情况下,循环返回false。将 (not ...) 表达式放在其父 (and ...) 表达式的末尾很重要,这样您就不会在检查任何期货是 true 或 false 之前遇到困难,直到它们全部实现。
这个新的解决方案可以概括为:
(defmacro future-or [& futures]
`(loop []
(cond (or ~@(for [f futures]
`(and (realized? ~f)
(deref ~f)))) true
(and ~@(for [f futures]
`(realized? ~f))
~@(for [f futures]
`(not (deref ~f)))) false
:else (recur))))
并且使用方式和上面future-or的例子一样。
现在,我对优化几乎一无所知。但据我所知,这肯定没有理论上的效率,因为一旦实现了任何给定的未来,就没有必要多次测试它的价值。好吧,这里有两个解决方案,我将其命名为 future-some。由于被测试的期货必须在每次循环迭代后动态变化,我不得不将它变成一个函数。这样,这种新方法类似于some,而不是or。实际上,我改变了行为以获取期货集合(而不是可变数量的单个参数——some 和 or 之间的另一个区别)。一种解决方案不进行双重检查(我认为):
(defn future-some [futures]
(if (empty? futures)
false
(let [res (reduce (fn [unrealized f]
(if (realized? f)
(if @f
(reduced true)
unrealized)
(cons f unrealized)))
()
futures)]
(if (true? res)
true
(recur res)))))
这里有很多细节需要说明,但要点是:如果没有要测试的期货,则返回false,否则,它会向下迭代期货列表,测试它们是否已实现。如果实现了未来,并且还取消了对true 的引用,则迭代中断并返回true。如果实现了未来但没有取消引用true,则迭代继续到下一项。如果未来未实现,则添加一个列表以用于future-some 的下一次递归。
另一种解决方案更简洁,但不太理想:
(defn future-some [futures]
(if (empty? futures)
false
(let [realized (filter realized? futures)]
(if (some deref realized)
true
(recur (remove (set realized) futures))))))
与另一个类似,除了它首先过滤掉已实现,然后测试,然后再次过滤(这次是反向 - 以获得未实现的),如果它需要重复。这第二个过滤是低效的步骤。
我提出的所有解决方案的一个问题是,未来的取消会在取消引用时导致错误,而他们可能应该简单地继续下去,就好像未来是错误的一样。这可以通过在任何取消引用之前将 (not (future-cancelled? ...)) 表达式放在每个 (and ...) 表达式中来解决。或者,对于future-some 函数,您必须将realized? 谓词替换为(some-fn (comp not future-cancelled?) realized?),或者为胆小的人替换#(and (not (future-cancelled %)) (realized? %))。
再次,说真的,感谢您提出这个问题。