【问题标题】:What do we call this (new?) higher-order function?我们把这个(新的?)高阶函数称为什么?
【发布时间】:2010-09-22 23:03:38
【问题描述】:

我试图说出我认为是高阶函数的新想法。最重要的是,这里是用 Python 和 Haskell 编写的代码来演示这个概念,这将在后面解释。

Python:

>>> def pleat(f, l):
       return map(lambda t: f(*t), zip(l, l[1:]))
>>> pleat(operator.add, [0, 1, 2, 3])
[1, 3, 5]

哈斯克尔:

Prelude> let pleatWith f xs = zipWith f xs (drop 1 xs)
Prelude> pleatWith (+) [0,1,2,3]
[1,3,5]

正如您可能推断的那样,序列正在被迭代,利用相邻元素作为您传递它的函数的参数,将结果投影到新序列中。那么,有人看过我们创建的功能吗?这对职能社区的人来说是不是很熟悉?如果不是,我们给它取什么名字?

---- Update ----

褶裥获胜!

Prelude> let pleat xs = zip xs (drop 1 xs)
Prelude> pleat [1..4]
[(1,2),(2,3),(3,4)]

Prelude> let pleatWith f xs = zipWith f xs (drop 1 xs)
Prelude> pleatWith (+) [1..4]
[3,5,7]

【问题讨论】:

  • 自然必须很短。所以犁在那里获胜。我的问题是它是否重要到值得一个名字。它有哪些应用程序,我们会经常使用它,以至于我们不会只使用单线 zipWith?
  • 我不知道它是否有名字,但我知道我已经遵循了很多这种模式。我很想知道它是否有一个通用名称。我通常将其称为“合并”,因为您将价值观合并在一起。
  • @lshpeck:基本库的快速search says no。它确实不属于基本库,因为它很容易重新创建但没有被广泛使用。我也会质疑为这样一个小功能添加包依赖项的价值。在 hackage MissingH 或 ListLike 是找到此类功能的两个可能的地方,但它不存在。
  • 别再想出一些没人会理解的可爱的小流行语来用于晦涩的、特殊目的的操作了。甚至不要考虑像“合并”这样的通用名称。使用描述性短语,例如。 apply_to_pairs.
  • 所以我在 Haskell Reddit 上发布了这个,他们很聪明 (reddit.com/r/haskell/comments/dhlso/…): let meld f = map (uncurry f) 。 (zipaptail)

标签: python haskell functional-programming theory higher-order-functions


【解决方案1】:

嗯……对位。

(`ap` tail) . zipWith

不值得拥有一个名字。

顺便说一句,水银说:

 zip`ap`tail

连续数字的阿兹特克神

【讨论】:

  • 为什么不呢?作为对位点,flip mapMfoldr (>>) (return ())liftM2 id 被归因于一些。 (注意我不是说应该,我是在指出这个论点很弱)
  • map 和 fold 是两个基本的高阶函数,因此,这样的使用非常普遍。 tail 比较罕见,zip ap tail 非常罕见。稀有 + 琐碎 == 不是命名的好理由。
  • 虽然我不知道这一点,但我认为其他人可能在同一条船上,无论他们使用 Haskell 多久。是否有任何地方通过一堆这些有用的代码 sn-ps,这些东西不像前奏代码那么常见但足够常见以制作这些帖子?还是大多数人自己都偶然发现了这一点?
  • 我见过理科研究生经常使用的这个高阶函数退出。主要在 MatLab 和 Mathematica 中,在 python 中出现一两次。它的知识要么是从曾经教过他们的人那里传下来的,要么是被重新发现的。
【解决方案2】:

既然它类似于“折叠”,但不会将列表折叠成单个值,那么“折痕”呢?如果你继续“折痕”,你最终会“折叠”(有点)。

我们可以用一个烹饪比喻,称之为“捏”,就像捏馅饼皮一样,尽管这可能暗示着圆形拉链,列表的最后一个元素与第一个元素配对。

def pinch(f, l):
    return map(lambda t: f(*t), zip(l, l[1:]+l[:1]))

(如果您只喜欢“crease”或“pinch”中的一个,请在评论中注明。这些应该是单独的建议吗?)

【讨论】:

  • 你可以做一个“infiniplow”,你可以通过不断地“犁”它来将序列减少到一个单一的值。迄今为止,我一直将其称为 PlowReduce。例如,PlowReduce 会将序列 [0..9] 犁到值 2304。
  • “犁”似乎不是一个很好的描述,因为犁会翻转、打碎和充气,这些似乎都不适用于函数的作用。
  • 我认为 pinch 是一个很好的人选!
【解决方案3】:

在 Python 中,meld 等效项位于 itertools 收据中,并被成对调用。

from itertools import starmap, izp, tee

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

所以我会这样称呼它:

def pairwith(func, seq):
    return starmap(func, pairwise(seq))

我认为这是有道理的,因为当您使用标识函数调用它时,它只是返回对。

【讨论】:

    【解决方案4】:

    如果l 也是生成器,这是 Python 的另一个实现

    import itertools as it
    
    def apply_pairwise(f, l):
        left, right = it.tee(l)
        next(right)
        return it.starmap(f, it.izip(left, right))
    

    我认为apply_pairwise 是一个更好的名字

    【讨论】:

    • +.5 表示名称,+.5 表示实施。
    【解决方案5】:

    我真的在 Python 的任何地方都看不到它的任何编码名称,这是肯定的。 “合并”很好,但可以在各种其他情况下使用。 “犁”往往未被使用,并提供了一个很好的视觉效果,可以稳定地穿过土壤。也许我只是花太多时间在园艺上。

    我还扩展了该原理以允许函数接收任意数量的参数。

    您也可以考虑:褶裥。它很好地描述了您列出列表(如一长串织物)并将其部分捆绑在一起的方式。

    import operator
    
    def stagger(l, w):
        if len(l)>=w:
            return [tuple(l[0:w])]+stagger(l[1:], w)
        return []
    
    def pleat(f, l, w=2):
        return map(lambda p: f(*p), stagger(l, w))
    
    print pleat(operator.add, range(10))
    print pleat(lambda x, y, z: x*y/z, range(3, 13), 3)
    print pleat(lambda x: "~%s~"%(x), range(10), 1)
    print pleat(lambda a, b, x, y: a+b==x+y, [3, 2, 4, 1, 5, 0, 9, 9, 0], 4)
    

    【讨论】:

    • 最好使用 itertools.starmap 而不是 map(lambda p: f(*p)...
    【解决方案6】:

    zipWithTailadjacentPairs

    【讨论】:

      【解决方案7】:

      我投票给smearWithsmudgeWith,因为这就像你在整个列表中涂抹/涂抹操作。

      【讨论】:

        【解决方案8】:

        这好像是 ruby​​ 的 each_cons

        ruby-1.9.2-p0 > (1..10).each_cons(2).to_a
        
        => [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10]] 
        

        【讨论】:

          【解决方案9】:

          这让我想起了图像处理中的卷积。但不确定这在数学上是否正确。

          【讨论】:

          • 在图像处理中,图像是表示为数组的函数。示例 'binaryProjection (+) [0,1,2,3]' 就像在 1D 图像上卷积 '[1,1]' 内核(首先将重叠系数相乘而不是总和)。尽管能够使用除“+”之外的任何其他函数使得所讨论的高阶函数更通用。
          • 这是一种卷积。卷积的两个函数是f 1 = 1, f 2 = 1和从自然数到输入列表的函数。二元运算由plowmeld 的函数参数指定或任何它被调用的。与二进制运算为 (+) 的信号卷积的 DSP 感觉非常相似。
          • @AaronMcSmooth,没问题。正如@sfty 所指出的,这是一种有趣的混合,主要类似于信号卷积,但由于二进制运算的不同而更通用。在更一般意义上的卷积(而不是受限的 DSP 版本)中,这种额外的普遍性并不是困难。不管怎样,@sfty 注意到了;不是我一个人做的。
          【解决方案10】:

          我认为这是普通 zip 版本的通用变体window。现在不在 ghci 终端,但我认为 window n = take n . tails。那么你的函数是zipWith (\[x,yj -> f x y) . window 2。当 f 是 [a] -> b 类型时,这种风格自然会更好。

          【讨论】:

            【解决方案11】:

            在 C++ 标准模板库中,它被称为adjacent_difference(虽然运算符可以是任何运算,而不仅仅是减法)

            【讨论】:

              【解决方案12】:

              因此,由于似乎没有名称,我建议使用“合并”或简单的“合并”,因为您将相邻的值合并在一起。

              所以合并已经完成,所以我现在建议“融合”(或“合并”,但可能太接近“合并”)

              例如:

              meld :: (a -> a -> b) -> [a] -> [b]
              meld _ [] = []
              meld f xs = zipWith f (init xs) (tail xs)
              

              可以用作:

              > meld (+) [1..10]
              [3,5,7,9,11,13,15,17,19]
              > meld compare "hello world"
              [GT,LT,EQ,LT,GT,LT,GT,LT,GT,GT]
              

              第二个例子没有真正的意义,但却是一个很酷的例子。

              【讨论】:

              • 我目前喜欢这个名字。我认为它具有足够的描述性。
              • 没关系,但与merge :: Ord a => [a] -> [a] -> [a] 冲突,后者将两个排序列表合并为一个排序列表。
              • @TomMD:好点,改为“融合”。它没有被拿走,我检查了它,它简短且具有描述性。 “只是,将这个列表与 (+) 函数融合在一起。”
              【解决方案13】:

              我很想将其称为 contour,因为我已将它用于音乐软件中的“轮廓”处理 - 当时我将其称为 twomap 或类似的傻东西。

              在音乐处理中还有两个特定的命名“轮廓”,一个是粗轮廓 - 音高是向上还是向下。另一个是精致的轮廓,轮廓要么向上,向下,向上跳跃或向下跳跃,但我似乎无法找到半音差异必须有多大才能进行跳跃的参考。

              【讨论】:

                【解决方案14】:

                使用 Mathematica

                Plus@@@Partition[{0, 1, 2, 3}, 2, 1] 或者这些更详细的替代方案中的任何一个

                Apply[Plus, Partition[{0, 1, 2, 3}, 2, 1], {1}]
                Map[Apply[Plus, #] &, Partition[{0, 1, 2, 3}, 2, 1]]
                

                我已经在许多语言中使用并享受过这个高阶函数,但我在 Mathematica 中最喜欢它;用 levelspec 选项分解为 Partition 和 Apply 看起来简洁灵活。

                【讨论】:

                • 我必须承认 Haskell 在 15 个月后的大部分时间里都赢得了我的青睐。
                【解决方案15】:

                很好的成语!我只需要在 Perl 中使用它来确定连续事件之间的时间。这就是我最终得到的结果。

                sub pinch(&@) {
                  my ( $f, @list ) = @_;
                  no strict "refs";
                
                  use vars qw( $a $b );
                
                  my $caller = caller;
                  local( *{$caller . "::a"} ) = \my $a;
                  local( *{$caller . "::b"} ) = \my $b;
                
                  my @res;
                  for ( my $i = 0; $i < @list - 1; ++$i ) {
                    $a = $list[$i];
                    $b = $list[$i + 1];
                    push( @res, $f->() );
                  }
                  wantarray ? @res : \@res;
                }
                
                print join( ",", pinch { $b - $a } qw( 1 2 3 4 5 6 7 ) ), $/;
                # ==> 1,1,1,1,1,1
                

                如果我让它依赖于 List::Util,实现可能会更漂亮,但是...嗯!

                【讨论】:

                • 恭喜,死灵法师 ;) 哦等等……死灵法师徽章,你需要 +5 才能得到这个答案……
                • 是的,Perl 很快就变丑了,叹息!
                【解决方案16】:

                BinaryOperate 或 BinaryMerge

                【讨论】:

                • 如果您打算将此限制为只允许接收两个参数的函数,“dyadic”将是一个更好的前缀。但是这里的原则,使用序列中的元素作为参数,理论上应该扩展到不仅仅是二元函数。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-05-24
                • 1970-01-01
                • 2019-05-06
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多