5

我熟悉zipWith对两个序列的相应元素进行操作的标准函数,但是在功能语言(或具有某些功能特征的语言)中,有条件地选择要压缩的元素对的最简洁方法是什么?第三序列?

当在 Excel 中划出一些东西时,这种好奇心就产生了。
对于 A1:A10、B1:B10、C1:C10、D1、E1 和 F1 中的数字,我使用如下公式:

{=AVERAGE(IF((D1<=(A1:A10))*((A1:A10)<=E1),B1:B10/C1:C10))}

IF 语句中的每一半乘法将产生一个布尔值数组,然后将它们相乘(AND'ed)在一起。这些布尔值控制最终将平均十个商中的哪一个,因此就好像正在评估十个单独的 IF 语句。

例如,如果 A1:A10 中的 10 个值中只有第二个和第三个值满足条件(>=D1 和 <=E1),那么公式最终会这样计算:

AVERAGE(FALSE,B2/C2,B3/C3,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE)

AVERAGE 函数恰好忽略了布尔值和文本值,所以我们只得到第二个和第三个商的平均值。

这可以用 Haskell 简洁地完成吗?二郎?LINQ 还是 F#?Python?ETC..

请注意,对于这个特定示例,上面给出的公式并不完全正确 - 它被缩写以了解基本点。当 A1:A10 中的十个元素都不满足条件时,则将十个 FALSE 值传递给 AVERAGE,这将错误地计算为 0。
公式应该这样写:

{=AVERAGE(IF(NOT(OR((D1<=(A1:A10))*((A1:A10)<=E1))),NA(),
             IF((D1<=(A1:A10))*((A1:A10)<=E1),B1:B10/C1:C10)))}

其中NA()产生错误,表明平均值未定义。

更新:

感谢您的回答。我意识到我的第一个问题非常简单,当第一个列表中的相应元素满足某些特定标准时,就对第二个和第三个列表中的元素对应用函数而言。我接受了 Norman Ramsey 的回答。

然而,我接下来要去的地方是想知道该函数是否可以应用于表示来自任意数量列表的相应元素的元组——因此我向 Lebertram 提出了关于zipWithN.

Apocalisp 关于应用函子的信息使我了解了有关 python 对参数列表进行解包的信息——将函数应用于任意数量的参数。

对于我上面给出的具体示例,平均列表元素的商(列表列表在哪里nums),看起来python可以这样做:

from operator import div

def avg(a): return sum(a,0.0)/len(a)
avg([reduce(div,t[1:]) for t in zip(*nums) if d<=t[0] and t[0]<=e])

更一般地,使用一个函数f和一个谓词p(连同avg),这变成:

avg([f(t[1:]) for t in zip(*nums) if p(t[0])])
4

3 回答 3

3

您正在寻找的是Applicative Functors。特别是链接论文中的“zippy”应用程序。

在 Haskell 表示法中,让我们调用您的函数f。然后使用应用程序编程,它看起来就像这样简洁:

f d e as bs cs = if' <$> ((&&) <$> (d <=) <*> (e >=))
                     <$> as <*> ((/) <$> bs <*> cs) <*> (repeat 0)
   where if' x y z = if x then y else z
         (<*>)     = zipWith ($)

结果f是一个列表。简单地取平均值。概括一下:

f g p as bs cs = if' <$> p <$> as <*> (((Some .) . g) <$> bs <*> cs)
                                  <*> (repeat None)

在这里,p 是一个谓词,所以你可以这样称呼它:

average $ fromMaybe 0 <$> f (/) ((&&) <$> (d <=) <*> (e >=)) as bs cs

...给出与上述相同的定义<*>

注意:我还没有测试过这段代码,所以可能会缺少括号等,但这可以理解。

于 2010-02-21T19:08:36.420 回答
3

如何有条件地选择 zip 中的元素?

先压缩,后选择。

在这种情况下,我使用 进行选择catMaybes,这在此设置中通常很有用。进行类型检查是一个巨大的痛苦(必须放在fromIntegral正确位置),但这是我要编写的代码,像往常一样依赖优化器:

average as bs cs d1 e1 = avg $ catMaybes $ zipWith3 cdiv as bs cs
  where cdiv a b c = if a >= d1 && a <= e1 then Just (b/c) else Nothing
        avg l = sum l / fromIntegral (length l)

函数cdiv代表“条件除法”。

要让catMaybes你必须导入Data.Maybe.

此代码类型检查,但我还没有运行它。

于 2010-02-21T23:41:08.870 回答
1

哈斯克尔:

average . map fromJust . filter isJust $ zipWith3 (\a b c -> if a >= d1 && a <= e1 then Just b/c else Nothing) as bs cs
  where average xs = let (sum,n) = foldl' (\(s,m) x -> (s+x,m+1)) (0,0) xs in sum / (fromIntegral n)
于 2010-02-21T17:14:16.360 回答