在Elm中获取列表的下一个元素的最佳方法
我目前正试图找出遍历列表的最佳方法。 我所说的穿越是什么意思 示例: 我有一个用户列表:在Elm中获取列表的下一个元素的最佳方法,elm,Elm,我目前正试图找出遍历列表的最佳方法。 我所说的穿越是什么意思 示例: 我有一个用户列表: userList : List User userList = [user, user, user, user] 我有一个currentUser,它必须是userList之外的用户 因此,我想要实现的是: 我想要类似List.getNext的东西,它获取userList和当前用户,并返回列表中相对于当前用户的下一个用户 这是我的实现。我认为这是非常复杂的-那么有没有人知道如何以更好的方式做这件事
userList : List User
userList =
[user, user, user, user]
我有一个currentUser,它必须是userList之外的用户
因此,我想要实现的是:
我想要类似List.getNext的东西,它获取userList和当前用户,并返回列表中相对于当前用户的下一个用户
这是我的实现。我认为这是非常复杂的-那么有没有人知道如何以更好的方式做这件事
traverseList : List a -> a -> Maybe (Maybe a)
traverseList list currentElement =
let
indexList =
List.indexedMap
(\index element ->
if element == currentElement then
index
else
-1
)
list
currentAsIndex =
let
mayBeIndex =
List.maximum indexList
in
case mayBeIndex of
Just index ->
index
Nothing ->
0
getWanted =
List.map
(\( id, element ) ->
if id == (currentAsIndex + 1) then
Just element
else
Nothing
)
(List.indexedMap (,) list)
|> List.filter
(\element ->
element /= Nothing
)
|> List.head
in
getWanted
说明:
我的方法是获取列表,为给定的列表创建一个索引列表(看起来像[-1,-1,-1,3,-1,-1])
然后我得到这个列表的最大值,因为这会给出当前用户在list.indexedMap中的位置
然后我将原始元素作为List.indexedMap进行迭代,找出下一个元素(在第4个示例中)并返回该元素。否则我什么也不退
然后我过滤这个空列表,只过滤一个用户,并使用List.head从列表中提取用户
结果是可能(可能用户)。。。那不太好。。。还是
感谢您提出的以更好的功能方式执行类似操作的想法
我真的想在函数式编程方面做得更好。这里有一个非常简单的递归解决方案:
getWanted : List a -> a -> Maybe a
getWanted list currentElement =
let findNextInList l = case l of
[] -> Nothing
x :: [] -> if x == currentElement
then List.head list
else Nothing
x :: y :: rest -> if x == currentElement
then Just y
else findNextInList (y :: rest)
in
findNextInList list
getNext : List a -> a -> Maybe a
getNext list element =
list
|> List.drop 1
|> zip list
|> List.filter (\(current, next) -> current == element)
|> List.map (\(current, next) -> next)
|> List.head
zip : List a -> List b -> List (a,b)
zip = List.map2 (,)
这里的想法是查看列表的前两个元素,如果第一个元素是当前元素,则使用第二个元素。如果没有,请使用列表的尾部重试
必须处理紧急情况(您可以为此函数编写至少4个单元测试):
- 根本找不到当前元素
- 当前元素是列表中的最后一个元素
- 列表可能为空
也许有更优雅的解决方案,但递归是函数式编程中非常常见的技术,因此我想与大家分享这种方法。如果您愿意导入,可以使用
import List.Extra as LE exposing ((!!))
getNext : a -> List a -> Maybe a
getNext item list =
list
|> LE.elemIndex item
|> Maybe.map ((+) 1)
|> Maybe.andThen ((!!) list)
如果找到的元素是列表中的最后一个元素,它将返回Nothing我最喜欢farmio基于库的方法,这也是我将来将使用的方法 也就是说,对于elm来说是新手,不需要查看它的库,我以前的方法如下:即为列表编制索引,如果搜索到任何项,则查找索引,如果有,则在下一个索引处获取项
nextUser : List User -> User -> Maybe User
nextUser lst usr =
let
numberedUsers =
List.map2 (\idx u -> ( idx, u )) (List.range 1 (List.length lst)) lst
usrIdx =
List.filter (\( idx, u ) -> u.name == usr.name) numberedUsers
|> List.head
|> Maybe.map Tuple.first
|> Maybe.withDefault -1
in
List.filter (\( idx, u ) -> idx == usrIdx + 1) numberedUsers
|> List.head
|> Maybe.map Tuple.second
另一种尽可能多地使用小提琴的方法是使用折叠
也许不必使用
列表
,只需使用数组
,它提供基于索引的访问。另一种可能的解决方案:
getWanted : List a -> a -> Maybe a
getWanted list currentElement =
let findNextInList l = case l of
[] -> Nothing
x :: [] -> if x == currentElement
then List.head list
else Nothing
x :: y :: rest -> if x == currentElement
then Just y
else findNextInList (y :: rest)
in
findNextInList list
getNext : List a -> a -> Maybe a
getNext list element =
list
|> List.drop 1
|> zip list
|> List.filter (\(current, next) -> current == element)
|> List.map (\(current, next) -> next)
|> List.head
zip : List a -> List b -> List (a,b)
zip = List.map2 (,)
我们删除列表的第一个元素,然后将其与原始列表压缩,以获得元组列表。元组的每个元素都包含当前元素和下一个元素。然后我们过滤该列表,从元组中提取“next”元素,并获取最终列表的开头。对于具体问题,有一些很好的答案。我想提出另一种模式,因为这种方法有一些缺点:
- 您依赖于检查用户的平等性。这限制了用户类型,例如,其中不能有函数
- 在不能保证所有用户都不同的列表中查找用户是脆弱的:如果列表恰好以
结束,则循环将永远不会到达[user1,user2,user1,user3]
user3
- 没有任何东西可以保证当前用户在列表中,因此其他答案的返回类型中的
,很难正确处理可能
type alias Model =
{ currentUser : User
, users : [User]
}
你要的是
rotateUser : Model -> Model
rotateUser model =
let
nextUser = Maybe.withDefault
model.currentUser
(getNext model.users model.currentUser)
in
{ model | currentUser = nextUser }
getNext : List a -> a -> Maybe a
...
与其实施getNext
,不如改变模型,让生活更轻松:
type alias Model =
{ currentUser : User
, otherUsers : [User]
}
rotateUser : Model -> Model
rotateUser model =
case model.otherUsers of
[] -> model
(nextUser::rest) ->
{ model
| currentUser = nextUser
, otherUsers = rest ++ [model.currentUser]
}
如果当前用户是列表中的最后一个用户,那么应该返回什么?没有,或者列表的第一个?当前在这个实现中,它返回第一个。对于我的用例,这是可以的。=>应该返回第一个元素。。。哈哈。。但我想知道为什么它返回第一个…)我不知道…啊好的。。。现在我明白了。因为-1列表的最大值是-1。后来我只是得到了头。。。所以它返回第一个元素。。。好啊这是好的,但不是有意的;-)这个很短很漂亮。谢谢你的建议。我不能标记多个解决方案,所以我更喜欢@stholzm的概念,它为我理解如何处理这些事情提供了良好的基础。谢谢太优雅了!我想你可以简单地添加“模列表长度”使其在所有情况下都能工作。我喜欢这种方法:)@marschro谢谢,但我承认它不是很简洁。我的代码中有五个case/if,很难一目了然。我自己也不喜欢,我已经怀疑我错过了什么。法米奥有一个更好的方法。显式递归有时仍然有用,但通常更倾向于使用高阶函数和组合。所以我希望你学到了一些关于递归的知识,并把这个答案看作是一个更具教育意义的答案。是的。确切地我把它作为一个学习的例子。我完全理解递归和列表解构