在 Haskell 中复制 Numpy 的高级 Indexing/Slicing

Replicating Numpy's Advanced Indexing/Slicing in Haskell

Numpy 在其数组访问运算符中具有复杂的 indexing/slicing/stepping 功能。看到这个:http://docs.scipy.org/doc/numpy/reference/arrays.indexing.html

在试验 Haskell 时,我认为尝试复制此索引功能的一个子集会很有教育意义。具体来说就是 "tuple selection objects" 或 n 维投影" (https://en.wikipedia.org/wiki/Projection_%28relational_algebra%29).

基本上你可以做到:

two_d_array[0:1:1, 0:4:2]

这将为您提供第一行步进 1,其中包含前 2 列步进 2(跳过 1 列)。

换句话说,它可以将一个原始的二维数组投影成一个更小的二维数组。结果仍然是二维数组。

这就是我在 Haskell 中尝试的方法。

这种函数的类型应该是这样的:

(!!!) :: (Functor f) =>  f a -> [(Int, Int, Int)] -> f a

所以你会看到像这样的东西:

three_d_tensor !!! [(s1,e1,st1), (s2,e2,st2), (s3,e3,st3)]

其中sx,ex,stx分别是start,end,step

该示例应将原始张量投影为较小的张量,第一维受 s1 to e1, stepping by st1 限制,第二维受 s2 to e2, stepping by st2 限制...等

这就是我得到的:

slicing from to xs = take (to - from + 1) (drop from xs)

stepping n = map head . takeWhile (not . null) . iterate (drop n)

(!!!) tensor ((start, end, step):tail) = 
    project (stepSlice start end step tensor) tail map
    where 
        project tensor ((start, end, step):tail) projection = 
            project (projection (stepSlice start end step) tensor) tail (projection . map)
        project tensor [] _ = 
            tensor
        stepSlice start end step tensor = 
            ((stepping step) . (slicing start end)) tensor

由于 "polymorphic recursion" 的问题,上述方法不起作用。基本上我不能无限地组合 map 函数,执行此操作的具体表达式是 (projection . map)。如果这种多态递归是可能的,我相信它会起作用。但我对不涉及多态递归的替代实现持开放态度。

我已经研究过这个问题,但仍然没有解决:

已经有一种用于从现有值计算新值的类型 - 函数。假设我们有一个索引到结构中的函数,我们可以通过将它应用于结构来使用它来索引结构。

(!!!) = flip ($)

infixr 2 !!!

如果我们有一个索引结构的函数和另一个索引任何嵌套结构的函数,我们可以通过 fmap 在结构上使用第二个函数然后应用外部函数来将它们组合在一起。

(!.) :: Functor f => (f b -> g c) -> (a -> b) -> f a -> g c
t !. f = t . fmap f

infixr 5 !.

以3d结构为例

three_d_tensor :: [[[(Int,Int,Int)]]]
three_d_tensor = [[[(x, y, z) | z <- [0..4]] | y <- [0..3]] | x <- [0..2]]

我们可以查看使用您的切片函数 slicingstepping 构建的精细切片。

example = three_d_tensor !!! slicing 1 2 !. stepping 2 !. stepping 2 . slicing 1 3

结果是

[[[(1,0,1),(1,0,3)],[(1,2,1),(1,2,3)]],[[(2,0,1),(2,0,3)],[(2,2,1),(2,2,3)]]]