In GNU Octave this code -
在GNU Octave中这段代码 -
[e, ix] = min(X);
will return minimum element and it's location. How do you this in repa for arbitrary binary function?
将返回最小元素及其位置。你如何在修复任意二进制函数?
This is what I came up with:
这就是我提出的:
min x = z $ foldl' f (e,0,0) es
where
(e:es) = toList x
f (a,ix,r) b = let ix' = ix+1 in if a < b then (a,ix',r) else (b,ix',ix')
z (a,ix,r) = (a,r)
In above example we convert repa 1D matrix to list and use foldl' (from Data.List) with two accumulators - one for counting iterations (ix) and other to save position of min element (r). But the whole point of using repa is to use arrays, not lists!
在上面的例子中,我们将repa 1D矩阵转换为list并使用foldl'(来自Data.List)和两个累加器 - 一个用于计算迭代(ix),另一个用于保存min元素(r)的位置。但是使用repa的全部意义是使用数组,而不是列表!
In repa there are two folds for Array type (foldS and foldP) - but they can only take function of type (a -> a -> a) - meaning, I cannot pass tuple with accumulators to it. There is also traverse, which can, in principle, reduce 1D array to a scalar array:
在repa中,Array类型有两个折叠(foldS和foldP) - 但它们只能取类型函数(a - > a - > a) - 意思是,我不能将带累加器的元组传递给它。还有遍历,原则上可以将1D数组减少为标量数组:
min x = traverse x to0D min
where
to0D (Z:.i) = Z
min f (Z) = ??? -- how to get elements for comparison?
The first thing that comes to mind is
首先想到的是
[f (Z:.i) | i <- [1..n]], where n = (\(Z:.i) -> i) $ extent x
But this will also convert array to list, instead of doing computation on the array.
但这也会将数组转换为列表,而不是在数组上进行计算。
2 个解决方案
#1
3
I'm no expert on Repa, but this seems to work for 1-D arrays. It can probably be adapted for other dimensions.
我不是维修专家,但这似乎适用于一维阵列。它可能适用于其他方面。
import Data.Array.Repa
indexed arr = traverse arr id (\src idx@(Z :. i) -> (src idx, i))
minimize arr = foldP f h t
where
(Z :. n) = extent arr
arr' = indexed arr
h = arr' ! (Z :. 0)
t = extract (Z :. 1) (Z :. (n-1)) arr'
f min@(valMin, iMin) x@(val, i) | val < valMin = x
| otherwise = min
#2
0
At the risk of reviving a zombie post, here is a arbitrary dimension solution I worked out (which, reviewing the comments, is exactly what @hammar suggested). Because of the weird nature of foldAllP
, which I use as an identity element when merging tuples, you also need to provide an upper bound for the minimum of the array you are seeking to find.
冒着恢复僵尸帖子的风险,这是我制定的任意维度解决方案(查看评论,正是@hammar建议的那样)。由于foldAllP的奇怪性质,我在合并元组时将其用作标识元素,因此您还需要为要搜索的数组的最小值提供上限。
import Data.Array.Repa
import Data.Array.Repa.Eval
import Data.Array.Repa.Repr.Unboxed
minimize :: (Shape sh, Source r a, Elt a, Unbox a, Monad m, Ord a) => Array r sh a -> a -> m (a,sh)
minimize arr upperBound = do
-- Zip the array with its (Int representation of) indices
let zippedWithIndex = Data.Array.Repa.traverse arr id (\f idx -> (f idx, toIndex (extent arr) idx))
-- Define a function that compares two tuple of (a,Int) on the value of the first entry
let minimumIndex t@(v,_) t'@(v',_) = if v < v' then t else t'
-- Do a parallel fold of all elements in the array
(min,idx) <- foldAllP minimumIndex (upperBound, error "Empty array") zippedWithIndex
-- convert the indice back from its Int representation
return (min, fromIndex (extent arr) idx)
Naturally, if your array contains a type that has an instance of Bounded
, you can make the more convenient function
当然,如果您的数组包含具有Bounded实例的类型,则可以使更方便的函数
minimize' arr = minimize arr maxBound
Some tests you can do at the prompt:
您可以在提示符处执行一些测试:
λ> let x = fromListUnboxed (ix2 2 2) [20, 30, 10, 40] :: Array U DIM2 Int
λ> minimize' x
(10,(Z :. 1) :. 0)
#1
3
I'm no expert on Repa, but this seems to work for 1-D arrays. It can probably be adapted for other dimensions.
我不是维修专家,但这似乎适用于一维阵列。它可能适用于其他方面。
import Data.Array.Repa
indexed arr = traverse arr id (\src idx@(Z :. i) -> (src idx, i))
minimize arr = foldP f h t
where
(Z :. n) = extent arr
arr' = indexed arr
h = arr' ! (Z :. 0)
t = extract (Z :. 1) (Z :. (n-1)) arr'
f min@(valMin, iMin) x@(val, i) | val < valMin = x
| otherwise = min
#2
0
At the risk of reviving a zombie post, here is a arbitrary dimension solution I worked out (which, reviewing the comments, is exactly what @hammar suggested). Because of the weird nature of foldAllP
, which I use as an identity element when merging tuples, you also need to provide an upper bound for the minimum of the array you are seeking to find.
冒着恢复僵尸帖子的风险,这是我制定的任意维度解决方案(查看评论,正是@hammar建议的那样)。由于foldAllP的奇怪性质,我在合并元组时将其用作标识元素,因此您还需要为要搜索的数组的最小值提供上限。
import Data.Array.Repa
import Data.Array.Repa.Eval
import Data.Array.Repa.Repr.Unboxed
minimize :: (Shape sh, Source r a, Elt a, Unbox a, Monad m, Ord a) => Array r sh a -> a -> m (a,sh)
minimize arr upperBound = do
-- Zip the array with its (Int representation of) indices
let zippedWithIndex = Data.Array.Repa.traverse arr id (\f idx -> (f idx, toIndex (extent arr) idx))
-- Define a function that compares two tuple of (a,Int) on the value of the first entry
let minimumIndex t@(v,_) t'@(v',_) = if v < v' then t else t'
-- Do a parallel fold of all elements in the array
(min,idx) <- foldAllP minimumIndex (upperBound, error "Empty array") zippedWithIndex
-- convert the indice back from its Int representation
return (min, fromIndex (extent arr) idx)
Naturally, if your array contains a type that has an instance of Bounded
, you can make the more convenient function
当然,如果您的数组包含具有Bounded实例的类型,则可以使更方便的函数
minimize' arr = minimize arr maxBound
Some tests you can do at the prompt:
您可以在提示符处执行一些测试:
λ> let x = fromListUnboxed (ix2 2 2) [20, 30, 10, 40] :: Array U DIM2 Int
λ> minimize' x
(10,(Z :. 1) :. 0)