如何在Elm中压缩形状奇怪的列表?
我想在我的Elm程序中结合两种数据结构。它们的类型是如何在Elm中压缩形状奇怪的列表?,elm,Elm,我想在我的Elm程序中结合两种数据结构。它们的类型是List(a,List b)和List c。这是在模型中存储数据的最简单方法,但我想编写一个函数,在显示数据之前将其转换为视图模型 type alias Model1 a b = List (a, List b) type alias Model2 c = List c type alias ViewModel a b c = List (a, List (b, c)) toViewModel : Model1 -> Model2 -
List(a,List b)
和List c
。这是在模型中存储数据的最简单方法,但我想编写一个函数,在显示数据之前将其转换为视图模型
type alias Model1 a b = List (a, List b)
type alias Model2 c = List c
type alias ViewModel a b c = List (a, List (b, c))
toViewModel : Model1 -> Model2 -> ViewModel
toViewModel model1 model2 =
???
toViewModel
函数应使用模型2的元素压缩模型1中的子列表。假设model2的大小与Model1中子列表的大小之和相同
例如,如果模型1的长度为2,而两个元素的子列表的长度为3和4,则假设模型2的长度为7。模型1的第一个元素的子列表应与模型2的前3个元素压缩,模型1的第二个元素的子列表应与模型2的下4个元素压缩
下面是前一个示例的示意图:
如何构造
toViewModel
函数?Elm没有内置的zip
函数,但很容易根据列表定义它。map2
:
zip:List a->List b->List(a,b)
zip=List.map2(,)
您不能单独在脱节列表上使用它,但您可以使用它来使用Model2中的第一个n
项压缩Model1中的子列表。要获取需要压缩的内容列表,您只需使用list。将作为压缩部分,并将作为列表。拖放以获取Model2中需要使用下一个Model1实体压缩的其余项目。这可以使用如下函数n来完成
toViewModel:model1ab->model2c->viewmodelabc
ToViewModelM1 m2=
案件m1
[] ->
[]
((m1ID,m1List)::rest)->
让
len=List.length m1List
init'=List.take len m2
尾部'=List.drop len m2
在里面
(m1ID,zip m1List init')::toViewModel rest tail'
请注意,使用zip
将在列表的第一个结尾处停止,因此如果列表长度不均匀,则项目将被删除
编辑-这里有一种额外的方法,其中显式递归被隔离,主要功能可以通过映射实现
解决这个问题的另一种方法是首先将Model2分组到一个列表列表中,其中每个项目包含对应Model1条目的适当数量的元素
为此,我将定义一个函数takes
,它将一个列表拆分为多个较小的列表,这些列表的大小作为第一个参数传入
takes:List Int->List a->List(List a)
接受计数列表=
个案数目
[] -> []
(x::xs)->List.take x List::take xs(List.drop x List)
现在,您可以使用takes
函数对Model2列表进行分组,允许您将其映射到Model1输入,以便压缩内部列表
toViewModel:model1ab->model2c->viewmodelabc
ToViewModel1模型2=
让
m2Grouped=takes(List.map)(List.length这是一个稍微简单一点的任务:
用于从Model1
将其与Model2
合并,以获得列表(a、b、c)
,其中包含来自两个列表的数据
按a对列表进行分组
以获得最终的list(a,list(b,c))
我不得不模拟一些数据来运行这里的测试
请考虑以下例子:
import Graphics.Element exposing (show)
-- List (a, List b)
type alias Model1 = List (String, List Int)
-- List c
type alias Model2 = List Bool
-- List (a, List (b, c))
type alias ViewModel = List (String, List (Int, Bool))
toViewModel : Model1 -> Model2 -> ViewModel
toViewModel model1 model2 =
let
-- Map merge both lists and map values to the grouping key.
mappedList =
model1
|> List.concatMap (\(groupBy, list) -> List.map (\el -> (groupBy, el)) list)
|> List.map2 (\val2 (groupBy, val1) -> (groupBy, (val1, val2))) model2
-- Extract a list for for specified grouping key.
getListByKey search =
List.filterMap
(\(key, val) -> if key == search then Just val else Nothing)
mappedList
in
List.map (\(search,_) -> (search, getListByKey search)) model1
initModel1 : Model1
initModel1 =
[ ("foo", [ 1, 2, 3 ])
, ("bar", [ 4, 5, 6, 7])
]
initModel2 : Model2
initModel2 =
[ True
, False
, True
, False
, True
, False
, True
]
main =
toViewModel initModel1 initModel2
|> show
我已经修复了类型别名。在我正在编写的实际代码中,它们是具体的类型,因此,我犯了一个错误,即出于示例目的,在泛化代码时忽略了它们。我修复了答案中的一个逻辑错误,并删除了现已过时的类型别名建议。这是您答案的准确、泛化摘要吗?“要组合形状奇怪的列表,请使用模式匹配和递归,因为zip=List.map2(,)
可能不够灵活。”模式匹配和递归对Elm非常重要,您不妨说“使用适合您需要的函数”。在像Elm这样的强类型语言中,您经常会听到这样一句话,“让类型引导您”。“由于类型不适合zip
函数,您必须定义自己的函数来压缩内容。它们是中心的,但级别较低。我希望我的问题的答案是某种折叠语句。Erik Meijer说过“递归是函数编程的目标。”我认为这意味着你的第一反应应该是在使用它之前寻找一个更高的抽象。这就是我在总结中试图解决的问题。