使用嵌套列表"压缩"普通列表

Mag*_*ist 1 haskell

我正在寻找一个优雅的解决方案来解决以下问题.我有两个以下类型的列表:

[Float] 和, [[Float]]

第一个列表包含无限量的随机值.第二个列表包含我不再关心的值.它的结构是有限的,必须保留.第一个列表的值需要替换第二个列表的值.

显然,由于第一个列表包含随机值,我不想使用它们两次.任何人都可以帮助我以清晰,简洁简洁的方式做到这一点吗?

scramble :: [Float] -> [[Float]] -> [[Float]]
Run Code Online (Sandbox Code Playgroud)

给我你最好的一击

sdc*_*vvc 9

使用拆分包进行拆分:

import Data.List.Split (splitPlaces)
scramble x y = splitPlaces (map length y) x
Run Code Online (Sandbox Code Playgroud)


pig*_*ker 8

这会吗?

flip . (evalState .) . traverse . traverse . const . state $ head &&& tail
Run Code Online (Sandbox Code Playgroud)

编辑:让我扩展建设......

它的基本中心是traverse . traverse.如果你用足够差的眼镜来盯着这个问题,你会发现它是"用容器容器的元素做些什么".对于那种事情,traverse(from Data.Traversable)是一个非常有用的小工具(好吧,我有偏见).

traverse :: (Traversable f, Applicative a) => (s -> a t) -> f s -> a (f t)
Run Code Online (Sandbox Code Playgroud)

或者,如果我改为更长但更具暗示性的类型变量

traverse :: (Traversable containerOf, Applicative doingSomethingToGet) =>
              (s -> doingSomethingToGet t) ->
              containerOf s -> doingSomethingToGet (containerOf t)
Run Code Online (Sandbox Code Playgroud)

至关重要的是,traverse保留其所经营的集装箱的结构,无论可能是什么.如果您将其traverse视为高阶函数,则可以看到它在类型符合其所需元素的运算符类型的容器上返回运算符.这就是说(traverse . traverse)有道理,并为您提出了结构保留的操作2个容器层.

traverse . traverse ::
  (Traversable g, Traversable f, Applicative a) => (s -> a t) -> g (f s) -> a (g (f t))
Run Code Online (Sandbox Code Playgroud)

因此,我们已经为列表列表中的结构保留"做某事"操作提供了关键小工具.该lengthsplitAt方法工作正常列表(列表的结构是由它的长度给出),但列出了允许这种做法的本质特征已经被相当多的瓶装Traversable类.

现在我们需要弄清楚如何"做某事".我们希望用从供应流中连续绘制的新东西替换旧元素.如果我们被允许更新供应的副作用,我们可以说在每个元素上做什么:" head供应的回归,用它来更新供应tail".该State s单子(在Control.Monad.State这是一个实例Applicative,从Control.Applicative)让我们抓住这一想法.该类型State s a表示a在改变类型状态的同时传递类型值的计算s.典型的此类计算由此小工具进行.

state     :: (s -> (a, s)) -> State s a
Run Code Online (Sandbox Code Playgroud)

也就是说,给定初始状态,只需计算值和新状态.在我们的例子中,s是一个流,head获取值,tail获得新状态.该&&&运营商(从Control.Arrow)是粘在同一数据的两个函数来获取制作一对功能的好方法.所以

head &&& tail :: [x] -> (x, [x])
Run Code Online (Sandbox Code Playgroud)

这使得

state $ head &&& tail :: State [x] x
Run Code Online (Sandbox Code Playgroud)

因此

const . state $ head &&& tail :: u -> State [x] x
Run Code Online (Sandbox Code Playgroud)

解释了对旧容器的每个元素"做"的内容,即忽略它并从供应流的头部获取一个新元素.

喂养到(traverse . traverse)让我们类型的大mutatey遍历

f (g u) -> State [x] (f (g x))
Run Code Online (Sandbox Code Playgroud)

在哪里fg是任何Traversable结构(例如列表).

现在,为了提取我们想要的函数,采用初始供应流,我们需要将状态变异计算解压缩为从初始状态到最终值的函数.这就是它的作用:

evalState :: State s a -> s -> a
Run Code Online (Sandbox Code Playgroud)

所以我们最终得到了一些东西

f (g u) -> [x] -> f (g x)
Run Code Online (Sandbox Code Playgroud)

如果要与原始规格相匹配,最好将其翻转.

TL;博士State [x]单子是用于描述读取和更新一个输入流计算的现成的工具.本Traversable类可捕获结构保留的操作对容器一个现成的概念.其余的是管道(和/或高尔夫球).

  • 谢谢.确实如此.这件事真的不挑剔.这可能是一个误导性的巧合,原始问题是从列表中获取并制作有序的结构.如果你想要一个很好的泛化,只需将它剥离回一个遍历,所以`(翻转.(evalState.).遍历.const.state $ head &&& tail)`适用于任何`Traversable`结构,而不仅仅是列表.`traversable`在组合`newtype C fgx = C(f(gx))`下关闭,因此您可以使用该事实在本地重新协商两个(或更多)容器层作为一个复合层而根本不改变遍历机制. (2认同)