Functional programming 仅使用不可变变量在函数式编程(Erlang)中存储用户输入信息

Functional programming 仅使用不可变变量在函数式编程(Erlang)中存储用户输入信息,functional-programming,erlang,Functional Programming,Erlang,作为一名Erlang的初学者,我正在学习Erlang编程书(第二版)。我很难掌握如何专门使用函数式编程原理存储和定期更新外部信息(如间歇性用户输入) 以我现在的例子来说,我现在在并发编程部分(第12章)的开头,书中谈到了区域服务器。下面是我的变体 作为练习,我尝试在这个模块中添加一种方法来存储用户发出的所有请求。但是,尽管有一点递归编程的经验,在命令式语言的意义上,可变变量的缺乏在这个特殊的例子中似乎是致命的 我尝试在SE站点上查找一些相关资源,如和 但它并没有以实际的方式回答我的问题。我知道,

作为一名Erlang的初学者,我正在学习Erlang编程书(第二版)。我很难掌握如何专门使用函数式编程原理存储和定期更新外部信息(如间歇性用户输入)

以我现在的例子来说,我现在在并发编程部分(第12章)的开头,书中谈到了区域服务器。下面是我的变体

作为练习,我尝试在这个模块中添加一种方法来存储用户发出的所有请求。但是,尽管有一点递归编程的经验,在命令式语言的意义上,可变变量的缺乏在这个特殊的例子中似乎是致命的

我尝试在SE站点上查找一些相关资源,如和 但它并没有以实际的方式回答我的问题。我知道,我试图实现的目标可以通过使用ETS(甚至是数据库)来实现,或者通过使用一个新进程的进程内存来实现,该进程接收并维护自身的历史记录

但我真正想理解的是(以及这个问题的要点)是否可以使用通用函数式编程原则而不必使用特定于Erlang的工具来实现。代码段中注释掉的行表示我天真地期望第一步是什么样子

  -module(geometry_server4).
  -export([start/0, client/2, loop/0]).

   start() ->
       spawn(geometry_server4, loop, []).

   client(Pid_server, Geom_tuple) ->
       Pid_server ! {self(), Geom_tuple},
       %ok = storerequests(Geom_tuple),
      receive
          {area, Pid_server, Area} -> io:format("Client: Area of ~p is ~p~n", [Geom_tuple, Area]);
          {error, Error} -> io:format("~p~n", [Error])
      end.

  %storerequests(Geom_tuple) -> addtolist(Geom_tuple, get_history()).
  %
  %addtolist(Item, History) ->
  %   [Item | History].
  %get_history() -> ???


  loop() ->
      receive
          {Client, {rectangle, S1, S2}} ->
              Area = S1 * S2,
              Client ! {area, self(), Area},
              loop();
          {Client, {square, S}} ->
              Area = S * S,
              Client ! {area, self(), Area},
              loop();
          {Client, _} ->
              Client ! {error, "invalid parameters"},
              loop()
      end.
根据本书,此玩具服务器在终端中被调用为:

1> c(geometry_server4).
2> P = geometry_server4:start().
3> geometry_server4:client(P, {square, 3}).
但我真正想理解的是什么(以及这一点) 问题)是否可以使用通用功能 无需使用Erlang特定工具的编程原则

是的,它可以。您可以使用循环变量来存储所谓的状态

首先,有几个初步要点:

  • 不要用行号发布代码。您希望有人能够复制您的代码并将其粘贴到他们的文本编辑器中,并能够运行代码

  • 在erlang中,按照惯例,变量名使用驼峰大小写,例如
    ServerPid

  • 出于理智考虑,不要使用长度超过两个字母的模块名称

  • 考虑将所有服务器代码放在文件的一部分,将所有客户端代码放在文件的另一部分。您的客户端代码位于服务器代码的中间。


  • 在外壳中:

    1> c(my).
    {ok,my}
    
    2> my:test().
    {area,<0.64.0>,6}
    {area,<0.64.0>,16}
    {history,<0.64.0>,[{square,4},{rectangle,2,3}]}
    {error,<0.64.0>,"invalid parameters"}
    {history,<0.64.0>,[{error,"hello"},history,{square,4},{rectangle,2,3}]}
    done
    
    3> 
    
    1>c(我的)。
    {好的,我的}
    2> my:test()。
    {区域,6}
    {区域,16}
    {历史,[{正方形,4},{矩形,2,3}]}
    {错误,,“无效参数”}
    {history,[{error,“hello”},history,{square,4},{rectangle,2,3}]}
    完成
    3> 
    
    确切地说,循环函数将接受一个参数,该参数将在每个循环中携带。@Yogesch,“一个循环变量”-这与我上面使用的循环函数类似吗?--很抱歉,我为您准备了一个示例,但我被一个愚蠢的错误挂断了电话。@Yogesch,这是一个很好的继续方法--努力一点总是能教会您更多。在函数式语言中,虽然t说变量是不可变的,但递归函数中的参数变量本质上是可变的。@Yogesch,在过去的几个月里,我也一直在慢慢地编写Erlang,并且我一直在github上发布我对练习的答案。我目前被困在第18章/Websockets上,因为书中的代码不再起作用了。@Yogesch,还有,如果你有时间,你应该在上查看免费的在线erlang课程。他们由编写Erlang编程的Simon Thompson教授,他们非常优秀。你会学到很多。我建议你按顺序学习这两门课程(先是功能课程,然后是并发课程),但功能课程目前正在运行,并发课程将在几周后开始。他们改变了那里的工作方式,所以我认为你可以得到一个免费课程,你可以参加考试,但是第二个课程你必须付费参加考试。
    1> c(my).
    {ok,my}
    
    2> my:test().
    {area,<0.64.0>,6}
    {area,<0.64.0>,16}
    {history,<0.64.0>,[{square,4},{rectangle,2,3}]}
    {error,<0.64.0>,"invalid parameters"}
    {history,<0.64.0>,[{error,"hello"},history,{square,4},{rectangle,2,3}]}
    done
    
    3>