我想针对以下问题编写一个Haskell函数:给出两个整数列表("深度"和"阈值"),返回平均深度和深度分数每个阈值大于或等于。
例如,给定深度[10,15,20]
和阈值[13.333333,1.0,0.5,0.16666667]
,预期结果应为-- getMeanAndPercents (thresholds, depths):
-- sum = 0
-- threshold_counts = [0 for t in thresholds]
-- len = 0
-- for d in depths:
-- sum += d
-- len += 1
-- for t in thresholds:
-- if d >= t: tresholds_counts[t] += 1
-- for t in tresholds
-- threshold_percents[t] = thresholds_counts[t] / len
-- return = [sum / len] + [threshold_percents]
(平均深度为13.3,深度的100%> = 10,深度的50% > = 15& 17%的深度> = 20)。
命令式伪代码解决方案可能如下所示:
getMeanAndPercents :: [Int] -> [Int] -> [Float]
getMeanAndPercents thresholds depths = let
depths2 = map f depths -- replace each depth d with [d, 1, 0, 1, 0, 1, 0, 0, ...]
counts = foldl1 (zipWith (+)) depths2 -- sum the inner lists
len = fromIntegral $ length depths -- get total depth length
in fromIntegral (head counts) / len : map (\c -> fromIntegral c / len) (tail counts)
where
f d = d : map (\t -> if d >= t then 1 else 0) thresholds
我的Haskell解决方案如下所示:
let ... in
问题:
1)如何缩进此代码(我不确定如何将where
与depths
混合)?
2)Haskell多久会循环length
?我假定foldl1 & map
一次,O(d*t)
一次。
3)在Haskell中实现什么更好(w.r.t.性能和/或可读性)? (我假设至少需要做>=
,即循环所有深度的所有阈值)
编辑:深度没有排序,阈值可以由函数排序。因此,一旦第一个阈值未通过测试,就可以跳过In [6]: df = pd.DataFrame(np.random.randint(100,size=(10,10))) \
.assign(Count=np.random.rand(10))
In [7]: df
Out[7]:
0 1 2 3 4 5 6 7 8 9 Count
0 89 38 89 68 6 32 22 12 70 69 0.225977
1 49 1 38 33 36 93 9 57 93 64 0.030430
2 2 53 49 79 80 86 26 22 31 41 0.629740
3 38 44 23 29 75 42 75 19 99 57 0.980604
4 45 2 60 74 35 77 46 43 63 55 0.136395
5 65 97 15 16 88 59 23 68 5 21 0.648485
6 95 90 45 75 24 32 72 76 32 75 0.703680
7 60 31 65 85 4 36 52 72 73 94 0.744026
8 64 96 96 15 75 22 20 68 56 39 0.500358
9 78 54 74 29 87 57 33 97 63 37 0.289975
。
答案 0 :(得分:1)
这是主观的 - 我个人更喜欢这个:
getMeanAndPercents :: [Int] -> [Int] -> [Float]
getMeanAndPercents thresholds depths
= let depths2 = map f depths
counts = foldl1 (zipWith (+)) depths2
len = fromIntegral $ length depths
in fromIntegral (head counts) / len : map (\c -> fromIntegral c / len) (tail counts)
where f d = d : map (\t -> if d >= t then 1 else 0) thresholds
Haskell中的列表实际上不是列表,而是懒惰的数据流,就像python等命令式语言中的生成器一样。
因为计算取决于流的长度,所以我们必须经历一次以查看它的长度,这也会迫使您将整个列表加载到内存中。
您可以为阈值列表中的每个项目循环深度列表一次,然后再次获取总和,这是O(2d+dt)
。
如果您确实知道列表的长度,则应将其传递给函数,或者您可以使用其他数据类型,如Array
,其长度已知。
我会写这样的实现:
getMeanAndPercents :: [Int] -> [Int] -> (Float, [Float])
getMeanAndPercents thresholds depths
| len <- fi $ length depths
, (c:cs) <- foldl1 (zipWith (+)) $ f <$> depths
= (fi c / len, (/len).fi <$> cs)
where f d = d : map (\t -> if d >= t then 1 else 0) thresholds
fi = fromIntegral
这更接近您的命令性代码:
getMeanAndPercents thresholds depths
| ((sum, len), ts) <- mapAccumL
( \(sum, len) d -> ( (sum+d, len+1)
, map (\t -> if d>=t then 1 else 0) thresholds
)
) (0,0) depths
= (/len) <$> (sum:foldl1(zipWith(+))ts)
这更具可读性/意识形态haskell:
getMeanAndPercents :: [Int] -> [Int] -> (Float, [Float])
getMeanAndPercents thresholds depths
| len <- length depths
= ( sum depths / len
, [ length (filter (>=t) depths) / len | t <- thresholds ]
)