【问题标题】:How would I convert this code to a functional program (as in the paradigm)?我如何将此代码转换为功能程序(如范例中)?
【发布时间】:2016-12-28 19:48:09
【问题描述】:

我必须根据单位价格和单位数量计算我的股票的总价值。我在 Python 中有以下代码:

prices = {
    "banana" : 4,
    "apple"  : 2,
    "orange" : 1.5,
    "pear"   : 3,
}
stock = {
    "banana" : 6,
    "apple"  : 0,
    "orange" : 32,
    "pear"   : 15,
}

for key in prices:
    print key
    print "price: %s" % prices[key]
    print "stock: %s" % stock[key]

total = 0
for key in prices:
    total = total + prices[key]*stock[key]
print total

我已经尝试通过用以下代码替换最后一个块来将其转换为功能更强大的程序:

def total(x):
    if len(x) == 1:
        return prices[prices.keys()[0]]*stock[prices.keys()[0]]
    else:
        return prices[prices.keys()[0]]*stock[prices.keys()[0]] + total(x[1:])

print total(prices)

上面的代码得到这个错误:

Traceback (most recent call last):
  File "python", line 30, in <module>
  File "python", line 28, in total
TypeError: unhashable type

有人可以将我的代码更正为功能更强大的编程版本吗?

【问题讨论】:

    标签: python functional-programming


    【解决方案1】:

    首先,让我们看一下命令式循环:

    total = 0
    for key in prices:
        total = total + prices[key]*stock[key]
    print total
    

    检查命令式循环,每次迭代都会改变的两件事是total,它本身很好,key,它源自prices.keys()。所以,我们需要这些东西。

    让我们尝试用自然语言重写命令式循环。我会选英文的。对于prices [或者应该是prices.keys()] 中的每个key,将总数增加prices[key]*stock[key]

    由于我们不能改变总数,让我们重写语句:

    对于prices.keys() 中的每个key,将运行总数增加prices[key]*stock[key]

    而且,由于prices.keys() 是水果的名称,让我们再写一遍:

    对于fruitNames 中的每个key,将运行总数增加prices[key]*stock[key]

    现在,这是我无法完全解释的精神跳跃。提示是 totalkey 在循环的每次迭代中都会发生变化。我们现在可以忽略total(因为我不会将其与尾递归优化进一步混淆)。对于功能样式,key 成为整个键列表,fruitNames

    def totalRecur(fruitNames):
    

    现在,让我们考虑一下基本情况。如果prices(和stock)为空怎么办?好吧,总数为零:

        if len(fruitNames) == 0:
            return 0
    

    看起来不错。现在,如果位置 0 只有一项呢?

        key = fruitNames[0]
        return prices[key] * stock[key]
    

    因为我们知道totalRecur([]) == 0,所以我们可以改为说

        return prices[key] * stock[key] + totalRecur([])
    

    而且,由于列表只有一项,我们知道fruitNames[1:] 是空列表:

        return prices[key] * stock[key] + totalRecur(fruitNames[1:])
    

    这应该为您提供足够的信息来编写totalRecur 的良好定义。

    【讨论】:

    • Python 中一般要避免递归。函数式 != 递归。
    • ??别这么戏剧化了。我只是说,如果您要就如何用特定语言编写代码提供建议,您不应该鼓励使用通常会导致性能下降的结构。 Python 不是为递归而构建的,分配新的堆栈帧会产生很多开销,并且没有尾调用优化。还有一个递归限制。在某些情况下,比如沿着你知道不是很深的树走下去,递归实现的简单性可能超过非递归实现。
    • 糟糕!我为我含糊的语气道歉。我根本没有试图变得戏剧化。我同意你的看法。我一直认为功能意味着大量使用递归。然后我想到了你说的,想起了我在 Python 3 中使用大量尾递归的一些麻烦,当超过最大递归深度时仍然会发出错误。
    • 不,不。道歉是我的。我误读了您的评论,说“StackOverflow [该网站] 糟透了”。
    • 无论如何,我的错误信息是错误的。它应该是RuntimeError: maximum recursion depth exceeded
    【解决方案2】:

    使用生成器表达式或列表/集合/字典推导非常实用:

    In [1]: prices = {
       ...:     "banana" : 4,
       ...:     "apple"  : 2,
       ...:     "orange" : 1.5,
       ...:     "pear"   : 3,
       ...: }
       ...: stock = {
       ...:     "banana" : 6,
       ...:     "apple"  : 0,
       ...:     "orange" : 32,
       ...:     "pear"   : 15,
       ...: }
       ...:
    
    In [2]: total_value = sum(stock[k]*prices[k] for k in stock)
    
    In [3]: total_value
    Out[3]: 117.0
    
    In [4]:
    

    【讨论】:

    • @GuillaumeJacquenot 我不同意编辑。粘贴交互式解释器会话的输出在 StackOverflow 上很常见。删除 In/Out 行实际上使它变得毫无意义,因为最后一行仅在 在交互式会话中 那样工作
    【解决方案3】:

    如果通过函数式编程,您的意思是使用高阶函数和 lambda:

    sum(map(lambda k, v: v * stock[k], prices.items()))
    

    您收到错误,因为表达式 x[1:] 是字典,而不是键

    【讨论】:

      【解决方案4】:

      您可以使用dict comprehension 来返回您想要的值而不会产生副作用,也可以避免状态和突变(参见Functional Programming):

      def total(prices, stock):
          return sum([p * stock[k] for k, p in prices.items() if k in stock])
      
      >>> total(prices, stock)
      >>> 117.0
      

      This answer 提供了灵感。

      【讨论】:

        【解决方案5】:
        def total(stock):
            return sum([v*prices[k] for k,v in stock.iteritems()]) #.iteritems() is a built in method for dicts. it returns key, value pairs
                                                                   #same as i, dict[i]
        

        【讨论】:

          猜你喜欢
          • 2017-10-04
          • 2015-07-07
          • 1970-01-01
          • 1970-01-01
          • 2020-09-02
          • 1970-01-01
          • 2010-10-09
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多