Oop 函数式编程语言是如何工作的?

Oop 函数式编程语言是如何工作的?,oop,programming-languages,haskell,functional-programming,paradigms,Oop,Programming Languages,Haskell,Functional Programming,Paradigms,如果函数式编程语言不能保存任何状态,它们如何做简单的事情,比如读取用户输入?他们如何“存储”输入(或存储任何相关数据?) 例如:这个简单的C语言如何翻译成Haskell这样的函数式编程语言 #include<stdio.h> int main() { int no; scanf("%d",&no); return 0; } #包括 int main(){ 国际贸易编号; scanf(“%d”和“否”); 返回0; } (我的问题受到这篇优秀文章的启

如果函数式编程语言不能保存任何状态,它们如何做简单的事情,比如读取用户输入?他们如何“存储”输入(或存储任何相关数据?)

例如:这个简单的C语言如何翻译成Haskell这样的函数式编程语言

#include<stdio.h>
int main() {
    int no;
    scanf("%d",&no);
    return 0;
}
#包括
int main(){
国际贸易编号;
scanf(“%d”和“否”);
返回0;
}

(我的问题受到这篇优秀文章的启发:。阅读它让我更好地理解了什么是面向对象编程,Java如何以一种极端的方式实现它,函数式编程语言是如何形成对比的。)

函数式编程源于lambda演算。如果你真的想了解函数式编程,请查看

这是一个“有趣”的方式来学习lambda微积分,并把你带入令人兴奋的函数编程世界

了解Lambda演算对函数式编程的帮助。

因此,LAMBDA演算是许多现实世界编程语言的基础,如LISP,Stand,ML,Haskell,. 假设我们想要描述一个函数,它向任何输入中添加三个,我们将写:

plus3 x = succ(succ(succ x)) 
阅读“plus3是一个函数,当应用于任何数字x时,产生x的后继数的后继数”

请注意,向任何数字添加3的函数不需要命名为plus3;名称“plus3”只是命名此函数的一个方便的缩写

plus3x)(成功0)≡ ((λx.(suc(suc(suc x)))(suc 0))

注意,我们使用lambda符号表示函数(我认为它看起来有点像鳄鱼,我猜这就是鳄鱼蛋的起源)

lambda符号是鳄鱼(a函数),x是它的颜色。你也可以把x看作一个参数(Lambda演算函数实际上只假设有一个参数),剩下的你可以把它看作函数的主体

现在考虑抽象:

g ≡ λ f. (f (f (succ 0)))
参数f用于函数位置(在调用中)。 我们称g为高阶函数,因为它接受另一个函数作为输入。 您可以将其他函数调用f视为“egs”。 现在,利用我们创建的两个函数或“Alligators”,我们可以执行以下操作:

(g plus3) = (λ f. (f (f (succ 0)))(λ x . (succ (succ (succ x)))) 
= ((λ x. (succ (succ (succ x)))((λ x. (succ (succ (succ x)))) (succ 0)))
 = ((λ x. (succ (succ (succ x)))) (succ (succ (succ (succ 0)))))
 = (succ (succ (succ (succ (succ (succ (succ 0)))))))
RealWorld pureScanf(RealWorld world, const char* format, ...);
如果你注意到,你可以看到我们的λf鳄鱼吃了我们的λx鳄鱼,然后λx鳄鱼就死了。然后我们的λx鳄鱼在λf的鳄鱼卵中重生。然后这个过程重复,左边的λx鳄鱼现在吃掉右边的另一只λx鳄鱼

然后你可以使用这组简单的规则来设计语法,函数式编程语言就诞生了

所以你可以看到,如果你知道Lambda演算,你就会理解函数式语言是如何工作的

哈斯克尔:

main = do no <- readLn
          print (no + 1)
main=do no函数式语言可以保存状态!他们通常只是鼓励或强迫你明确地这样做

例如,查看Haskell的。

(一些函数式语言允许使用不纯函数。)

对于纯函数式语言,真实世界的交互通常包括在函数参数中,如下所示:

(g plus3) = (λ f. (f (f (succ 0)))(λ x . (succ (succ (succ x)))) 
= ((λ x. (succ (succ (succ x)))((λ x. (succ (succ (succ x)))) (succ 0)))
 = ((λ x. (succ (succ (succ x)))) (succ (succ (succ (succ 0)))))
 = (succ (succ (succ (succ (succ (succ (succ 0)))))))
RealWorld pureScanf(RealWorld world, const char* format, ...);
不同的语言有不同的策略将世界从程序员那里抽象出来。例如,Haskell使用monad隐藏
world
参数


但是函数式语言本身的纯部分已经是图灵完备的,这意味着任何在C中可行的东西在Haskell中也是可行的。命令式语言与命令式语言的主要区别在于,它没有修改现有的状态:

int compute_sum_of_squares (int min, int max) {
  int result = 0;
  for (int i = min; i < max; ++ i)
     result += i * i;  // modify "result" in place
  return result;
}
如果函数式编程语言不能保存任何状态,它们如何做一些简单的事情,比如读取用户的输入(我的意思是它们如何“存储”它),或者存储任何相关数据

正如您所知,函数式编程没有状态,但这并不意味着它不能存储数据。区别在于如果我写一个(Haskell)语句

let x = func value 3.14 20 "random"
in ...
我保证
x
的值在
..
中始终相同:任何东西都不可能改变它。类似地,如果我有一个函数
f::String->Integer
(一个获取字符串并返回整数的函数),我可以确保
f
不会修改其参数,也不会更改任何全局变量,也不会将数据写入文件,等等。正如sepp2k在上面的评论中所说,这种不可变性对于程序的推理非常有帮助:您编写的函数可以折叠、旋转和破坏您的数据,返回新的副本以便将它们链接在一起,并且您可以确保这些函数调用都不会做任何“有害”的事情。你知道
x
总是
x
,你不必担心有人在
x
的声明和它的使用之间写了
x:=foo bar
,因为这是不可能的

现在,如果我想从用户那里读取输入,该怎么办?正如肯尼特所说,这个想法是一个不纯函数是一个纯函数,它作为一个参数传递整个世界,并返回其结果和世界。当然,你不想这样做:一方面,它非常笨重,另一方面,如果我重复使用同一个世界对象会发生什么?所以这就被抽象了。Haskell使用IO类型处理它:

main :: IO ()
main = do str <- getLine
          let no = fst . head $ reads str :: Integer
          ...
第一个允许我们讨论什么都不做的IO操作:
return 3
是一个IO操作,它不查询真实世界,只返回
3
>=
操作符,发音为“bind”,允许我们运行IO操作。它从IO操作中提取值,通过函数传递值和真实世界,并返回结果IO操作。请注意,
>=
强制执行我们的规则,即
main = getLine >>= \str -> let no = (fst . head $ reads str :: Integer) in ...