numpy - 数组 - haskell重现numpy

给定一个平面列表,将它重设为n维列表:


import numpy as np



a = np.arange(1, 18)


b = a.reshape([-1, 2, 3])



# b = 


# 


# array([[[ 1, 2, 3],


# [ 4, 5, 6]],


# 


# [[ 7, 8, 9],


# [10, 11, 12]],


# 


# [[13, 14, 15],


# [16, 17, 18]]])




我能够使用固定索引来重现该行为,例如:


*Main> reshape23 [1..18]


[[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]],[[13,14,15],[16,17,18]]]



我的代码是:


takeWithRemainder :: (Integral n) => n -> [a] -> ([a], [a])


takeWithRemainder _ [] = ([], [])


takeWithRemainder 0 xs = ([], xs)


takeWithRemainder n (x:xs) = (x : taken, remaining)


 where (taken, remaining) = takeWithRemainder (n-1) xs



chunks :: (Integral n) => n -> [a] -> [[a]]


chunks _ [] = []


chunks chunkSize xs = chunk : chunks chunkSize remainderOfList


 where (chunk, remainderOfList) = takeWithRemainder chunkSize xs



reshape23 = chunks 2 . chunks 3



现在,我找不到方法把这个推广到任意形状,我最初的想法是:


reshape :: (Integral n) => [n] -> [a] -> [b]


reshape ns list = foldr (n acc -> (chunks n) . acc) id ns list



但是我总是从编译器得到类型错误。

时间:

这里有两个与python定性不同的细节。

你注意到自己的第一个:在每个分块步骤中,结果类型与输入类型不同,这意味着不能使用foldr,因为它需要一个特定类型的函数,通过递归实现。

第二个问题不太明显:reshape函数的返回类型取决于第一个参数是什么,然后返回类型为[2],在Haskell中,所有类型必须在编译时已知,这意味着reshape函数不能接受运行时定义的第一个参数,换句话说,第一个参数必须位于类型级别。

类型级别值可以通过类型函数(又称作"type familys")计算,但因为它不只是类型(例如,还有一个要计算的值),这个类型的自然(还是唯一)机制,

首先,定义类型类:


class Reshape (dimensions :: [Nat]) from to | dimensions from -> to where


 reshape :: from -> to



类有三个参数:kind dimensions[Nat]是一个类型级别的数字数组,表示所需的维度,from是参数类型,to是结果类型,请注意,即使已知参数类型始终是lt(

列表列表与数组不同,这是一个常见的误解,Haskell没有数组,你不得不处理列表

Haskell生态系统中有两个数组库,其中一个是massiv

reshapenumpy的功能可以通过resize'函数实现:


λ> 1 ... (18 :: Int)


Array D Seq (Sz1 18)


 [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 ]


λ> resize' (Sz (3 :> 2 :. 3)) (1 ... (18 :: Int))


Array D Seq (Sz (3 :> 2 :. 3))


 [ [ [ 1, 2, 3 ]


 , [ 4, 5, 6 ]


 ]


 , [ [ 7, 8, 9 ]


 , [ 10, 11, 12 ]


 ]


 , [ [ 13, 14, 15 ]


 , [ 16, 17, 18 ]


 ]


 ]



...