我试图通过使用一个函数生成Vector
的元组,该函数从索引创建值的自定义数据类型(或元组)。这是一种实现预期结果的方法:
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
除了我想在每个Vector的单次迭代中完成它,几乎就像下面所示,但避免多次评估函数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
概念的解决方案,其他库有非常相似的构造函数(例如,Repa有fromFunction
),我在这里使用Vector
只是为了演示一个问题。
也许某种类型的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)
与向量相同,fusion会在性能上节省一天,但仍然需要一个中间数组arr
的元组,我试图避免这种情况。
编辑2 :( 3年后)
在上面的Repa示例中,它不会创建中间数组,因为fromFunction
会创建一个延迟数组。相反,它将更糟糕的是,它将为每个索引评估f
两次,第一个数组为第一个数组,第二个数组为第二个数组。必须计算延迟数组,以避免这种重复工作。
答案 0 :(得分:1)
回想一下几年前我自己的问题,我现在可以轻松地展示出我正在尝试做的事情,而不是如何完成它。
简而言之,这不可能纯粹地完成,因此我们需要求助于两个向量的ST
monad和手动突变,但是最后我们确实得到了仅创建两个向量的好函数和纯函数并且不依赖融合。
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])
答案 1 :(得分:0)
你要写的重要内容是
splat f = unzip . fmap f
分享两个结果向量之间评估f
的结果,但是你想避免使用中间向量。不幸的是,我很确定你无论如何都无法在任何有意义的意义上拥有它。为简单起见,考虑长度为1的向量。为了使结果向量共享f (v ! 0)
的结果,每个都需要对表示该结果的thunk的引用。好吧,那个thunk必须 某个地方,它确实可能在一个向量中。