我试图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
为每个索引计算两次,一个用于第一个数组,第二次用于第二个数组。必须计算延迟数组以避免这种重复工作。