一切都可以建模为功能吗?

时间:2012-11-01 09:03:52

标签: functional-programming

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

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

每当我想打开或关闭灯泡时,我都会将is_turned_on更改为1,并将light_intensity从1(最暗)设置为10(最亮)。

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

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

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

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

更新:我通过c2 Wiki - Functional Programming找到了对上述问题的回答

  

数据项如何保留?

     

在堆栈上。在顺序批处理程序中,数据被初始化并且   转换为顶级功能。在像一个长寿的计划   服务器,顶级循环函数被递归调用,传递   全球国家从一个电话到下一个电话。

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

通过defparametersetf改变变量是不是更有效,更简单?想象一下,如果它不是一个灯泡,而是一个更复杂的对象,有更多的信息?我如何将其建模为函数?

2 个答案:

答案 0 :(得分:2)

  

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

我认为你正在从命令性观点看函数式编程,这种情况发生了很多并且可能令人困惑。在函数式编程中,而不是将程序表示为一系列修改状态的步骤(例如,通过设置变量),它表示为一组相互依赖的数学样式函数,每个函数只包含一个表达式。因为在函数中包含多行的唯一原因是修改状态,在纯函数式编程中,所有函数都是单行;这意味着代码作为一系列级联函数调用运行。与逐步说明相比,程序更像是对问题的描述。

  

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

我认为所有函数式编程语言都使用垃圾收集。较少的副作用和内存混叠意味着在不再使用内存时更容易计算出来。

  

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

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

答案 1 :(得分: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