问题描述
我刚刚开始学习Erlang,因为发现不存在for循环,所以我尝试使用递归方法重新创建一个:
display (房间在)->
Room = array:get(In,Rooms)
io:format(〜w,[Room]),如果
In< 59->显示(房间,In + 1);
true->真正的
结尾。
使用此代码,我需要显示Rooms中每个数组的内容(false或true),直到达到59。但是,这将创建一个奇怪的代码,该代码显示所有房间内容约60次(?)。当我放下if语句并只放入递归代码时,它会工作,除了异常错误:错误的参数。
所以基本上我的问题是我该如何放置一个
预先感谢!
这是对递归循环定义的普遍误解。您尝试检查的内容称为基本条件或基本案例。通过匹配以下内容最容易解决:
display(0,_)->
ok;
显示(在房间中)->
Room = array:get(In,Rooms)
io:format(〜w〜n,[Room]),
display(In-1,Rooms)。
但是,这很简单。
而不是使用手工的递归函数,更常见的是折叠或贴图。
但是,大多数人可能会选择将房间表示为集合或列表,并使用列表操作对其进行迭代。手写时,基本情况将是一个空列表而不是0:
display([])-> ;
ok;
display([Room | Rooms])->
io:format(〜w〜n,[Room]),
display(Rooms)。
本来可以避免再次支持:
display(Rooms)->
列表:foreach(fun(Room)-> io:format(〜w〜n,[Room])结尾,房间)。
有些人真的不喜欢这样在线阅读lambda。 (在这种情况下,我认为它是可读的,但它们越大,就越有可能真正分散注意力
display(Rooms)->
Display = fun(Room)-> io:format(〜w〜n,[Room])结尾,
列出:foreach(Display,Rooms)。
这本身可能会被忽略,而倾向于使用列表理解作为迭代的简写:
_ = [io:format(〜w〜n,[Room])|房间<-房间]。
当 only 试图产生副作用时,我真的认为出于语义原因, lists:foreach / 2
是最佳选择。
我认为您遇到的困难之一就是经验是您选择使用一个非常不寻常的结构作为您的第一个执行任何操作的Erlang程序的基础数据(数组不经常使用,并且在功能语言中不是很惯用)。首先尝试使用列表-这并不可怕-并且一些习惯用法和其他代码示例以及有关列表处理和函数式编程的一般讨论将更有意义。
等等!还有更多...
我没有处理您房间布局不规则的情况。始终假设所有内容都被安排在一个均匀的网格中-进入真正有趣的东西绝不是这种情况(因为地图不规则或拓扑有趣)。
这里的主要区别在于,与其简单地携带 [Room]
的列表,其中每个 Room
value是表示房间状态的单个值,您可以将房间的状态值包装在一个元组中,该元组还包含有关该状态的一些额外数据,例如其位置或坐标,名称等。(您知道, 元数据-今天是一个如此繁琐,繁琐的术语,我讨厌这样说。)
假设我们需要保持三维坐标房间所在的空间,并且每个房间都有一个占用者列表。在数组的情况下,我们将数组除以布局的尺寸。一个10 * 10 * 10的空间将具有一个从0到999的数组索引,并且每个位置都可以通过类似于
locate({X,Y,Z})-> (1 * X)+(10 * Y)+(100 * Z)。
,每个 Room
的值将是 [Occupant1,occupant2,...]
。
定义这样的数组和然后将其任意大的区域标记为不可用,以给人以不规则布局的印象,然后解决该问题,尝试模拟3D宇宙。
相反,我们可以使用一个列表(或类似列表的东西)来代表房间的集合,但是 Room
的值现在将是一个元组: Room = {{X ,Y,Z},[乘员]}
。您可能还有一个额外的元素(或十个!),例如房间的名称或其他状态信息等,但是坐标是您可能会获得的最确定的真实身份。要获取房间状态,您可以像以前一样进行操作,但要标记要查看的元素:
display(Rooms) ->
Display =
fun({ID,Occupants})->
io:format( ID〜p:居住者〜p〜n,[ID,居住者])
结尾,
列表:foreach(展示,房间)。
要执行比顺序打印更有趣的事情,可以替换的内部组件显示
,该函数使用坐标在图表上绘制房间,检查乘员
的空列表或完整列表(使用模式匹配, ,或者您可能梦dream以求的其他任何事情。
I just started learning Erlang and since I found out there is no for loop I tried recreating one with recursion:
display(Rooms, In) ->
Room = array:get(In, Rooms)
io:format("~w", [Room]),
if
In < 59 -> display(Rooms, In + 1);
true -> true
end.
With this code i need to display the content (false or true) of each array in Rooms till the number 59 is reached. However this creates a weird code which displays all of Rooms contents about 60 times (?). When I drop the if statement and only put in the recursive code it is working except for a exception error: Bad Argument.
So basically my question is how do I put a proper end to my "for loop".
Thanks in advance!
This is a general misunderstanding of recursive loop definitions. What you are trying to check for is called the "base condition" or "base case". This is easiest to deal with by matching:
display(0, _) ->
ok;
display(In, Rooms) ->
Room = array:get(In, Rooms)
io:format("~w~n", [Room]),
display(In - 1, Rooms).
This is, however, rather unidiomatic. Instead of using a hand-made recursive function, something like a fold or map is more common.
Going a step beyond that, though, most folks would probably have chosen to represent the rooms as a set or list, and iterated over it using list operations. When hand-written the "base case" would be an empty list instead of a 0:
display([]) ->
ok;
display([Room | Rooms]) ->
io:format("~w~n", [Room]),
display(Rooms).
Which would have been avoided in favor, once again, of a list operation like foreach:
display(Rooms) ->
lists:foreach(fun(Room) -> io:format("~w~n", [Room]) end, Rooms).
Some folks really dislike reading lambdas in-line this way. (In this case I find it readable, but the larger they get the more likely the are to become genuinely distracting.) An alternative representation of the exact same function:
display(Rooms) ->
Display = fun(Room) -> io:format("~w~n", [Room]) end,
lists:foreach(Display, Rooms).
Which might itself be passed up in favor of using a list comprehension as a shorthand for iteration:
_ = [io:format("~w~n", [Room]) | Room <- Rooms].
When only trying to get a side effect, though, I really think that lists:foreach/2
is the best choice for semantic reasons.
I think part of the difficulty you are experiencing is that you have chosen to use a rather unusual structure as your base data for your first Erlang program that does anything (arrays are not used very often, and are not very idiomatic in functional languages). Try working with lists a bit first -- its not scary -- and some of the idioms and other code examples and general discussions about list processing and functional programming will make more sense.
Wait! There's more...
I didn't deal with the case where you have an irregular room layout. The assumption was always that everything was laid out in a nice even grid -- which is never the case when you get into the really interesting stuff (either because the map is irregular or because the topology is interesting).
The main difference here is that instead of simply carrying a list of [Room]
where each Room
value is a single value representing the Room's state, you would wrap the state value of the room in a tuple which also contained some extra data about that state such as its location or coordinates, name, etc. (You know, "metadata" -- which is such an overloaded, buzz-laden term today that I hate saying it.)
Let's say we need to maintain coordinates in a three-dimensional space in which the rooms reside, and that each room has a list of occupants. In the case of the array we would have divided the array by the dimensions of the layout. A 10*10*10 space would have an array index from 0 to 999, and each location would be found by an operation similar to
locate({X, Y, Z}) -> (1 * X) + (10 * Y) + (100 * Z).
and the value of each Room
would be [Occupant1, occupant2, ...]
.
It would be a real annoyance to define such an array and then mark arbitrarily large regions of it as "unusable" to give the impression of irregular layout, and then work around that trying to simulate a 3D universe.
Instead we could use a list (or something like a list) to represent the set of rooms, but the Room
value would now be a tuple: Room = {{X, Y, Z}, [Occupants]}
. You may have an additional element (or ten!), like the "name" of the room or some other status information or whatever, but the coordinates are the most certain real identity you're likely to get. To get the room status you would do the same as before, but mark what element you are looking at:
display(Rooms) ->
Display =
fun({ID, Occupants}) ->
io:format("ID ~p: Occupants ~p~n", [ID, Occupants])
end,
lists:foreach(Display, Rooms).
To do anything more interesting than printing sequentially, you could replace the internals of Display
with a function that uses the coordinates to plot the room on a chart, check for empty or full lists of Occupants
(use pattern matching, don't do it procedurally!), or whatever else you might dream up.
这篇关于Erlang递归结束循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!