如何使用Clojure以功能方式解决这种编程情况?

时间:2014-10-23 01:34:57

标签: algorithm clojure functional-programming

我有一个编程问题,我知道如何在Ruby中解决,但不知道Clojure中的最佳方法(并且可能有一种优雅的方式来实现这一功能)。

问题可以简化为:


我有一个装满水的3升水桶。在桶的底部有一个孔,即泄漏10毫升/秒(即需要300秒/ 5分钟才能清空)。我有一杯100毫升的水,我可以用来将新水倒入桶中。

我只能将玻璃杯的全部内容倒入桶中,不会倾倒。倾倒瞬间发生。

计划出一组时间步骤,我可以将水倒入桶中。


我知道有一种非常明显的方法可以使用代数进行此操作,但实际问题涉及随时间变化的“泄漏率”,以及并非总是等于100 mL的“新玻璃体积”,因此不是很难以封闭的形式解决。

解决这个问题的Ruby方法是使用“Bucket instance”跟踪铲斗的音量,并在多个时间步骤测试以查看铲斗是否有100 mL的空间。如果是这样,倾倒玻璃,并添加到“桶实例”中的水。继续时间步骤,观察水桶容量。

我希望我所描述的内容很清楚。

2 个答案:

答案 0 :(得分:5)

函数式编程最重要的概念之一是任何没有外部副作用的突变都可以卸载到不可变的函数参数绑定中。

这里模拟的时间和桶的级别是主要的功能参数,并且它们针对每个递归调用进行更新。其他参数被建模为时间函数。我们可以将这些函数中的每一个实际上视为基于时间增量的惰性序列,就像fill-times函数本身一样。或者使用向量中的查找建模的分段线性方程式,或者什么样的。

user> 
(defn fill-times
  [time level
   {:keys [sample-rate calc-bucket-size calc-leak-rate calc-glass-size]
    :as params}]
  (loop [t time l level]
    (let [input-capacity (calc-glass-size time)
          bucket-capacity (calc-bucket-size time)
          has-room (> (- bucket-capacity l) input-capacity)
          leak-delta (* (calc-leak-rate) sample-rate -1)]
      (if has-room
        (lazy-seq (cons t (fill-times t (+ l input-capacity)
                                      params)))
        (recur (+ t sample-rate) (+ l leak-delta))))))

#'user/fill-times
user> (take 50 (fill-times 0 0 {:sample-rate 1
                                 :calc-bucket-size (constantly 3000)
                                 :calc-leak-rate (constantly 10)
                                 :calc-glass-size (constantly 100)}))
(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 11 21 31 41 51 61 71 81 91 101 111 121 131 141 151 161 171 181 191 201)

当有足够的空间时,玻璃被倾倒(当然立即被填满),我们记下时间,再次调用该功能以获得以下时间。当没有空间时,我们重复,更新时间和桶级别。结果是玻璃可以被清空的(假设无限的)懒惰序列(假设玻璃立即被填充,并立即倾倒)。

答案 1 :(得分:3)

我对Clojure没有很多经验,但想到这一点的一种方法是在时间步长上的状态值的懒惰seq。懒惰地从先前的状态值计算每个状态值。

这是一个递推方程,也称为差分方程。它将新值计算为先前值的函数,而不会覆盖它们。

状态值可以只是桶级别或持有(time,bucket_level,pour_in_count)的元组。