【问题标题】:How to write this expression functionally?如何在功能上编写这个表达式?
【发布时间】:2018-03-20 22:19:37
【问题描述】:

我想在功能上重写以下python代码:

lst = [0.0] * 10
for pos in [2,5]:
    lst[pos] = 1.0

这是我的尝试,但不正确:

lst = (lambda zeros:
          map(
              lambda pos: zeros[pos] = 1.0, [2,5])
      )([0.0] * 10)

我得到的错误是lambda cannot contain assignment

使用列表推导,解决方案是:

lst = [ 1.0 if x in [2,5] else 0.0 for x in range(10) ]

【问题讨论】:

  • 怎么不正确?它会抛出异常或给出错误的答案吗?无论哪种方式,将其包含在您的问题中
  • lst = [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0] ;)
  • 您的列表理解更实用,因为它不会破坏输入。函数式编程的一个非常重要的组成部分是不变性和引用透明性。这确保表达式是可互换的。即使在 Haskell 中,列表推导也非常方便且非常实用。

标签: python lambda functional-programming


【解决方案1】:

我确实更喜欢您自己的答案中的列表理解,但如果我要使用更多的功能性触摸来做到这一点,我想我会使用 lambda 函数和 map

lazy = map(lambda element: 1 if element in [2,5] else 0, range(10))

请注意,map 在 Python 3 中是一个惰性迭代器。如果您想要评估的 list,您必须将该行包含在显式的 list(...) 构造函数中:

lst = list(map(lambda element: 1 if element in [2,5] else 0, range(10)))

【讨论】:

    【解决方案2】:

    列表推导方法可能更好,但如果您正在寻找函数式的方法:

    lst = [0.0] * 10
    lst = map(lambda (i, x): 1.0 if i in {2,5} else x, enumerate(lst))
    #[0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0]
    

    通过使用enumerate(lst) 作为map() 的可迭代对象,我们可以获得索引和值。在这种情况下,如果 i{2, 5} 中,我们将产生 0。否则我们保留值x

    如果您使用的是 python3,则必须使用 list() 包装对 map() 的调用。但是,这种方法是 not recommended,因为它会浪费地创建一个列表。

    【讨论】:

      【解决方案3】:

      另一种方法是使用泛型函数unfold

      def unfold (f, acc):
        return f ( lambda x, next: [x] + unfold (f, next)
                 , lambda x: [x] 
                 , acc                          
                 )
      
      def main (ones):
        def value (i):
          return 1 if i in ones else 0   
        return unfold ( lambda next, done, i:
                          done (value (i)) if i >= 10 else next (value (i), i + 1)
                      , 0
                      )
      
      print (main ( { 2, 5 }))
      # [0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0]
      

      unfold 可以以有趣的方式使用

      def alphabet ():
        return unfold ( lambda next, done, c:
                          done (c)            
                            if c == 'z' else
                              next (c, chr (ord (c) + 1))
                      , 'a'
                      )
      
      print (alphabet ())
      # ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
      

      Python 的 lambda 语法非常僵化,其笨拙的三元语法使函数表达式难以编写。在下面的示例中,我们使用一些命令式语法预先定义了一个函数 gen 以提高可读性,然后我们将其传递给 unfold - 该程序还表明 state 可以是一个复杂的值

      def fib (n):
        def gen (next, done, state):
          (n, a, b) = state
          if n == 0:
            return done (a)
          else:
            return next (a, (n - 1, b, a + b))
        return unfold (gen, (n, 0, 1))
      
      print (fib (20))
      # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
      

      当然,所有这些痛苦的存在都是有原因的——这是一个信号,表明我们做错了什么。一个经验丰富的 python 程序员永远不会像我们上面那样写mainalphabetfib。所以它不是 pythonic(正如他们所说),而是 functional,希望能回答你的问题。


      下面,我们极大地简化了unfold - 我们不是将nextdone 帮助器传递给用户lambda,而是要求用户返回一个对其选择进行编码的元组:(value, None)value 是序列中的最后一个值,(value, nextState) 将产生下一个值并继续下一个状态。

      这里的权衡是unfold 不那么复杂,但它需要用户知道特殊的元组信令来编写他们的程序。之前,nextdone 让用户无需担心这种担忧。无论哪种方式都可以,我分享这个只是为了提供另一种选择

      def unfold (f, acc):
        (x, nextAcc) = f (acc)
        if nextAcc is None:
          return [x]
        else:
          return [x] + unfold (f, nextAcc)
      
      def fib (n):
        def gen (state):
          (n, a, b) = state
          if n == 0:
            return (a, None)
          else:
            return (a, (n - 1, b, a + b))
        return unfold (gen, (n, 0, 1))
      
      print (fib (20))
      # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-08-11
        • 1970-01-01
        • 1970-01-01
        • 2011-09-05
        • 1970-01-01
        • 1970-01-01
        • 2017-11-06
        • 1970-01-01
        相关资源
        最近更新 更多