1

让我们考虑一个例子:我想打开/关闭一个灯泡。在 C 中,我可以写:

struct lightbulb {
    int is_turned_on;
    /* from 1 to 10 */
    int light_intensity;
};

每当我想打开或关闭灯泡时,我is_turned_on通过设置light_intensity从 1(最暗)到 10(最亮)来更改为 1 和亮度。

我如何在函数式编程中做同样的事情?我想我必须创建一个列表来保存这些值,创建一个函数ONOFF“打开”/关闭灯泡,以及一个返回灯泡光强度的函数。每次调用该函数时,都会返回一个新灯泡:

(defun turn-on()
  '(1 0))
(defun turn-off()
  '(0 0))
(defun light-intensity (x)
  `(0 ,(eval x)))

我可以看到像光强度这样的函数是一个类似于线性函数的连续函数。无论我们x对每个 x 传递多少次相同的参数,它都会计算出相同的结果。每个功能的结果是一个具有不同状态的新灯泡。

问题是,我怎样才能坚持状态?显然,我必须通过变量将它存储在我记忆中的某个地方。

更新:我通过c2 Wiki - 函数式编程找到了上述问题的答案

数据项如何持续存在?

在堆栈上。在顺序批处理程序中,数据在顶级函数中进行初始化和转换。在像服务器这样的长期程序中,递归调用顶级循环函数,将全局状态从一个调用传递到下一个调用。

每次调用函数时我还必须创建一个新对象(列表),如何销毁之前的旧对象?

defparameter通过and改变变量不是更有效更简单setf吗?想象一下,如果它不是一个灯泡,而是一个包含更多信息的更复杂的物体?如何将其建模为函数?

4

2 回答 2

2

问题是,我怎样才能坚持状态?显然,我必须通过变量将它存储在我记忆中的某个地方。

我认为您正在从命令式的角度看待函数式编程,这种情况经常发生并且可能会令人困惑。在函数式编程中,它不是将程序表示为一系列修改状态的步骤(例如通过设置变量),而是表示为一组相互依赖的数学风格的函数,每个函数只包含一个表达式。因为在函数中包含多行的唯一原因是修改状态,所以在纯函数式编程中,所有函数都是单行的;这意味着代码作为一系列级联函数调用运行。程序往往被视为对问题的描述,而不是分步说明。

每次调用函数时我还必须创建一个新对象(列表),如何销毁之前的旧对象?

我认为所有函数式编程语言都使用垃圾收集。更少的副作用和内存混叠意味着当内存不再被使用时更容易解决。

通过defparameter和setf改变变量不是更有效更简单吗?想象一下,如果它不是一个灯泡,而是一个包含更多信息的更复杂的物体?如何将其建模为函数?

我不确定你在这里问什么。

于 2012-11-01T10:29:21.367 回答
1

我在问如何使用纯函数式编程有效地处理状态,而不需要另一个副本只是为了使其“引用透明”?

只要语言支持线性类型,就可以在函数式编程中有效地处理状态。也就是说,每个可变单元格都被赋予一个线性类型,并且类型检查器会确保任何线性类型的变量都不会按照程序员的意愿被丢弃或复制。例如,这是不允许的:

val x = make_reference (5) // [x] is a mutable cell
val y = x
val res = !x + !y // the syntax [!x] is for reading a value of a cell

这是不允许的,因为 [x] 具有线性类型,并且线性类型的值不能重复(这实际上是我们在下一行将 [y] 绑定到 [x] 时所做的)。这种重复也称为“别名”(或“共享”),而别名又使状态操作程序更难推理(例如,通过破坏引用透明度)。因此,线性类型限制了别名,这有助于推理程序。大多数情况下,具有线性类型的程序在引用上是透明的,并且与纯函数程序保持一些相似性。

这是 ATS 中的一个示例,它使用线性类型来处理(可变)状态。

typedef lightbulb (b: bool) = @{is_turned_on= bool b, light_intensity= intBtw (1, 10)}

fn lightbulb_make {b:bool} (state: bool b, intensity: intBtw (1, 10)) :<> lightbulb b =
  @{is_turned_on= state, light_intensity= intensity}

// The [&T1 >> T2] notation means that function expects to be given
// a term of type T1, and then on exit, the type of the term will
// change to T2.
// In our case, the function expects a lightbulb either turned on or off,
// but on exit, the lightbulb will be turned off.
fn lightbulb_turn_on {b:bool} (x: &lightbulb b >> lightbulb true) :<> void =
  x.is_turned_on := true

fn lightbulb_change_intensity {b:bool} (x: &lightbulb b, y: intBtw (1, 10)) :<> void =
  x.light_intensity := y

implement main () = let
  var bulb = lightbulb_make (false, 5)
  val () = lightbulb_turn_on (bulb)
  val () = lightbulb_change_intensity (bulb, 3)
in
  printf ("intensity is now: %d\n", @(bulb.light_intensity))
end
于 2012-11-01T10:29:02.117 回答