1

我试图Vector通过使用从索引创建自定义数据类型(或元组)值的函数来生成 s 的元组。这是一种达到预期结果的方法:

import Prelude hiding (map, unzip)
import Data.Vector hiding (map)
import Data.Array.Repa
import Data.Functor.Identity

data Foo = Foo {fooX :: Int, fooY :: Int}

unfoo :: Foo -> (Int, Int)
unfoo (Foo x y) = (x, y)

make :: Int -> (Int -> Foo) -> (Vector Int, Vector Int)
make n f = unzip $ generate n getElt where
  getElt i = unfoo $ f i

除了我想在每个向量中进行一次迭代之外,几乎就像下面显示的那样,但要避免对函数进行多次评估f

make' :: Int -> (Int -> Foo) -> (Vector Int, Vector Int)
make' n f = (generate n getElt1, generate n getElt2) where
  getElt1 i = fooX $ f i
  getElt2 i = fooY $ f i

就像一个注释,我知道Vector库支持融合,第一个例子已经很有效了。我需要一个generate概念的解决方案,其他库有非常相似的构造函数(fromFunction例如 Repa),我在Vector这里使用 s 只是为了演示一个问题。

也许某种f函数调用的记忆会起作用,但我想不出任何东西。

编辑:

使用 Repa 的问题的另一个演示:

makeR :: Int -> (Int -> Foo) -> (Array U DIM1 Int, Array U DIM1 Int)
makeR n f = runIdentity $ do
  let arr = fromFunction (Z :. n) (\ (Z :. i) -> unfoo $ f i)
  arr1 <- computeP $ map fst arr
  arr2 <- computeP $ map snd arr
  return (arr1, arr2)

与向量相同,融合节省了性能,但仍然需要一个中间arr元组数组,我试图避免这种情况。

编辑2:(3年后)

在上面的 Repa 示例中,它不会创建中间数组,因为fromFunction会创建延迟数组。相反,它会更糟,它会f为每个索引计算两次,一个用于第一个数组,第二次用于第二个数组。必须计算延迟数组以避免这种重复工作。

4

2 回答 2

1

回顾几年前我自己的问题,我现在可以轻松地展示我试图做的事情以及如何完成它。

简而言之,它不能纯粹地完成,因此我们需要求助于STmonad 和两个向量的手动变异,但最终我们确实得到了这个只创建两个向量并且不依赖于融合的漂亮而纯粹的函数。

import Control.Monad.ST
import Data.Vector.Primitive
import Data.Vector.Primitive.Mutable

data Foo = Foo {fooX :: Int, fooY :: Int}

make :: Int -> (Int -> Foo) -> (Vector Int, Vector Int)
make n f = runST $ do
  let n' = max 0 n
  mv1 <- new n'
  mv2 <- new n'
  let fillVectors i
        | i < n' = let Foo x y = f i
                   in write mv1 i x >> write mv2 i y >> fillVectors (i + 1)
        | otherwise = return ()
  fillVectors 0
  v1 <- unsafeFreeze mv1
  v2 <- unsafeFreeze mv2
  return (v1, v2)

我们以类似的方式使用它generate

λ> make 10 (\ i -> Foo (i + i) (i * i))
([0,2,4,6,8,10,12,14,16,18],[0,1,4,9,16,25,36,49,64,81])
于 2018-12-18T10:27:24.257 回答
0

你想要写的最重要的东西是

splat f = unzip . fmap f

它共享两个结果向量之间的评估f结果,但您希望避免中间向量。不幸的是,我很确定你不能以任何有意义的方式同时拥有它。为简单起见,考虑一个长度为 1 的向量。为了让结果向量共享 的结果f (v ! 0),每个向量都需要一个对表示该结果的 thunk 的引用。嗯,那个 thunk 必须某个地方,而且它实际上也可能在一个向量中。

于 2016-01-25T07:56:08.877 回答