您可能没有注意到,但是距离我上次发布博客已经过去了几个星期。 原因是我的Soleus骨折了,而我的腿也石膏了。 不能动弹,我认为调查完全不同的东西是个好主意–要么看那天的电视,要么,尽管Kojak和Magnum PI的重播很诱人,但对Erlang的调查却名列前茅。
这里要记住的是,这不是
Erlang教程 ,这里的想法是研究Erlang和Java之间的一些相似之处,以尝试为学习Erlang提供一个起点。 如果我犯了任何啸叫错误,那么希望有更多Erlang经验的人会告诉我。 入门时,他们首先告诉您有关Erlang的信息,它是一种功能语言。 但是,在您陷入中风之前,它是一种结构良好的功能语言,以为您正在处理对象。
那是什么意思 在Java中,代码存储在表示类的文件中,类是执行单一职责的一组数据和方法。 您可以实例化一个类并通过变量访问其方法,也可以通过其类名访问其静态方法。
在Erlang中,代码存储在称为模块的文件中,每个模块都是执行一项职责的一组功能。 您无法实例化模块,并且实例变量和类变量不存在。 您只能使用方法参数变量。 您可以通过其模块名称来访问方法,就像访问Java静态方法一样。 与Java类一样,Erlang模块具有私有和公共功能。
作为Java开发人员,我很高兴发现有一个
eclipse的Erlang插件 。 这是因为学习一种语言而不是一种语言和一套全新的开发工具会更快。 几个月前,我与几位Erlang顾问进行了交谈,他们说他们更喜欢使用emacs,而我确实想知道为什么要等到我发现eclipse插件才算不错。 仍然足以开始使用,并且它具有潜力。
Java开发人员应该有其他相似之处:Erlang模块源文件被编译成.beam
文件,然后在Erlang虚拟机上运行; 还有eunit ,Erlang相当于JUnit ,还有一个log4erl ,顾名思义,是Erlang的Log4J版本。 使用edoc (Erlang的Javadoc版本)和标准项目布局可以自动生成文档,这与Maven布局非常相似,如下所示:
其结构与Maven略有不同: 目标目录称为ebin ,并且src和test目录已在项目目录级别进行了拆分,但是易于遵循,您已习惯了。
到目前为止,您已经认为Java和Erlang之间的最大区别是Java文件具有.java
扩展名,而Erlang文件具有.erl
扩展名。 不幸的是,还有更多的东西,首先是没什么大不了的,或者是Erlang看起来很奇怪的语法1 。
为了对此进行调查,我认为我将使用现有的ShoppingCart
和ShoppingCartTest
类并将其转换为Erlang。 我的telldontask
项目中提供了这两个类,它们看起来像这样……
ShoppingCart类
public class ShoppingCart { private final List<Item> items; private PaymentMethod method; public ShoppingCart() { items = new ArrayList<Item>(); } public void addItem(Item item) { items.add(item); } public double calcTotalCost() { double total = 0.0; for (Item item : items) { total += item.getPrice(); } return total; } public double calcTotalCost(double shipping, double minShippingAmount) { double totalCost = calcTotalCost(); if (totalCost > minShippingAmount) { totalCost += shipping; } return totalCost; } public void setPaymentMethod(PaymentMethod method) { this.method = method; } public void pay(double shipping, double minShippingAmount) { double totalCost = calcTotalCost(shipping, minShippingAmount); method.pay(totalCost); }
}
ShoppingCartTest JUnit
public class ShoppingCartTest { /** * Test method for {@link tell_dont_ask.ask.ShoppingCart#getAllItems()}. */ @Test public void calculateTotalCost() { ShoppingCart instance = new ShoppingCart(); Item a = new Item("gloves", 23.43); instance.addItem(a); Item b = new Item("hat", 10.99); instance.addItem(b); Item c = new Item("scarf", 5.99); instance.addItem(c); double totalCost = instance.calcTotalCost(); assertEquals(40.41, totalCost, 0.0001); } }
上面的代码演示了一些非常基本的购物车功能。 但是,有关这些类如何工作的更多详细信息,请参阅“ 定义告诉不要问”和“ 分解告诉不要问” 。
Erlang中的等效代码如下所示:
shopping_cart模块
-module(shopping_cart). %% ====================================================================
%% API functions
%% ====================================================================
-export([add_item/2,calc_total_cost/1,calc_total_cost/3,pay/3]). %% @doc Add an item to the order list
add_item(OrderList,Item) -> [Item | OrderList]. %% @doc Calculate the total cost of all the items in a list. The List must have the following format:
%% [{itemName, Price}]
%% where
%% itemName -> atom
%% Price -> float()
calc_total_cost(OrderList) -> round_dp(calc_total_cost(0,OrderList)). %% @doc Calculate the total cost of all the items in a list adding a shipping cost if the value is below a certain limit.
%% The Order List must have the following format:
%% [{itemName, Price}]
%% where
%% itemName -> atom
%% Price -> float()
calc_total_cost(OrderList,Shipping, MinShippingAmount) -> Cost = calc_total_cost(OrderList), TotalCost = Cost + shipping(Cost,Shipping,MinShippingAmount), round_dp(TotalCost). %% @doc @todo Method not implemented
pay(_Order,_Shipping, _MinShippingAmount) -> unimplemented. %% ====================================================================
%% Internal functions
%% ==================================================================== calc_total_cost(Result,[{_,Price} | TheRest]) -> calc_total_cost(Result + Price,TheRest);
calc_total_cost(Result,[]) -> Result. shipping(Cost,Shipping,MinShippingAmount) when Cost < MinShippingAmount -> Shipping;
shipping(_,_,_) -> 0. round_dp(Number) -> List = float_to_list(Number,[{decimals,2}]), list_to_float(List).
shopping_cart_tests模块
-include_lib("eunit/include/eunit.hrl"). -module(shopping_cart_tests). %% ====================================================================
%% API functions
%% ====================================================================
-export([]). %% @doc Calculate total code - written to match the Java style
calculate_total_cost_test() -> EmptyList = [], OrderList1 = shopping_cart:add_item(EmptyList,{gloves,23.43}), OrderList2 = shopping_cart:add_item(OrderList1,{hat,10.99}), OrderList3 = shopping_cart:add_item(OrderList2,{scarf,5.99}), ?_assertEqual(40.42,shopping_cart:calc_total_cost(OrderList3)). %% @doc Calculate total cost example - written in a better erlang style
calculate_total_cost_2_test() -> OrderList = [{gloves,23.43},{hat,10.99},{scarf,5.99}], ?assertEqual(40.41,shopping_cart:calc_total_cost(OrderList)).
专家告诉我,使用Erlang可以用更少的代码行做更多的事情。 这里不是这种情况,但是我又添加了很多注释行(用'%'分隔符表示)。
与Java相比,上面的代码看起来很奇怪(因为我没有从Erlang到HTML的转换器,所以看上去也很丑)。 没有任何实例变量,因此所需的任何数据都作为函数参数传入。 如果看一下add_item(…)
您会发现它将Item
变量添加到列表的开头,例如: items.add(item)
(请注意,在Erlang中,变量名始终以大写字母开头)。
转到calc_total_cost()
,事情开始看起来真的很奇怪…… calc_total_cost(OrderList)
只是calc_total_cost(0,OrderList)
的包装。 calc_total_cost(0,OrderList)
是对calc_total_cost(Result,[{_,Price} | TheRest])
或calc_total_cost(Result,[])
的调用,它们是循环执行从列表中添加商品价格的函数。 除非它不循环; Erlang中没有for循环,您必须使用递归,在calc_total_cost(Result,[{_,Price} | TheRest])
逐步添加价格,然后递归调用自身,直到列表为空。
关于Erlang语法的问题是,尽管对于使用C
派生语言的Java开发人员来说,它是非常合乎逻辑的,因此很容易上手。
要注意的一件事是,上面的Erlang是模仿Java编写的。 这可能不是您从头开始开发Erlang购物车的方式。
为什么选择Java而不是Erlang? 当然不是因为它与Java相似。 当Java的功能和优点可以帮助您更有效,更经济地解决问题时,您会选择Erlang而不是Java。 LenartÖhman的Google Tech Talk Erlang指出,目标应用程序必须是:
- 容错
- 马不停蹄
- 同时
- 分布,可扩展和异构
- 软实时
- 需要“原型性”
这些目标已经以多种方式实现。 例如,进程之间传递的消息是语言的一部分,而不是单独的API。 要将消息发送到另一个进程,只需键入:
Pid ! theMessage,
…其中Pid
是将接收theMessage
的进程的ID。 要发送消息,请输入在另一个Erlang虚拟机上运行的另一个进程:
Pid ! theMessage,
从这个不愉快的笑话中,我可能已经猜到Erlang虚拟机上的进程是透明的。 这意味着它与运行它们的机器上的Erlang进程没有什么不同。 无论是在本地还是在不同的物理硬件上。 这是因为Erlang虚拟机可以互相通信并且可以集群。 JVM之外的某种方式。
要接收消息,请使用Erlang关键字: receive
,如下所示:
%% @doc Receive a message and print the contents
print() -> receive Message -> io:format("The message is: ~p~n",[Message]) end.
进程本身非常轻巧,旨在以其他语言无法模仿的方式在大型多核处理器上利用超线程。 流程在Erlang中非常重要,我在某处读到,如果Java是一种面向对象的语言,那么Erlang就是一种面向过程的语言。
如果此博客读起来像是Erlang的广告,那是因为在将我的脚趾浸入水中之后,我可以看到有很多问题可以比基于Java的解决方案更便宜,更轻松地解决我当前的项目。 另一方面,Java和Spring(此博客的常见主题)更适合这些问题。 计算机语言只是简单的工具,您应该选择适合自己的最佳语言。
1很奇怪,您是否基于Prolog而不是C而成为Java程序员。
可从Captain Debug Github存储库中获得此博客的Java代码示例:
https://github.com/roghughe/captaindebug ,而Erlang代码可从我的Erlang Samples Github存储库中获得: https://github.com/roghughe/erlang_samples 。
翻译自: https://www.javacodegeeks.com/2013/08/erlang-for-java-developers.html