如何在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 -

我想在我的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 -> 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说过“递归是函数编程的目标。”我认为这意味着你的第一反应应该是在使用它之前寻找一个更高的抽象。这就是我在总结中试图解决的问题。