在函数式编程中有几个工具可以做到这一点,比如折叠和列表/生成器推导,但最基本的想法是递归。
因此,例如,对于 getTotal():
getTotal(sides, dice):
if dice == 0:
return 0
else
return rand.int(sides) + getTotal(sides, dice - 1)
遗憾的是,此函数不适用于您的特定示例,因为堆栈将在我们到达结束之前很久就溢出。我们通过使函数尾递归来解决这个问题,因此您可以使用尾调用优化(并非所有语言都使用此功能,但几乎所有函数式语言都使用)。这是通过添加一个以 0 开头的累加器变量来实现的:
getTotal(sides, dice, accumulator = 0):
if dice == 0:
return accumulator
else
return getTotal(sides, dice - 1, accumulator + rand.int(sides))
在这个版本的函数中,递归调用是一个“尾调用”,这意味着它是函数做的最后一件事(在以前的版本中不是这种情况,加法是最后发生的事情,所以它不是尾递归)。版本可以以相同的getSixes()
方式完成,除了不只是将掷骰子添加到累加器中,当且仅当掷骰为 6 时,我们才将 1 添加到累加器中。
但是,这种模式很常见,以至于许多语言都有“快捷方式”,即fold。因为我不想发明一堆符号,所以我将用 Python 编写下一个示例:
def getTotal(sides, dice):
add = lambda x,y: x + y
return reduce(add, (rand.int(sides) for _ in range(dice)))
reduce
是 python 的左折叠名称,add
是我们要折叠的函数。该(rand.int(sides) for _ in range(dice))
部分是一个生成器,它会懒惰地(即我们需要它们,而不是一开始就一次)生成你的随机数,特别dice
是它们的数量。
函数式编程的另一个技巧是根据一些布尔函数“过滤”值。我们可以getSixes()
这样解决:
def getSixes(sides, dice):
condition = lambda x: x == 6
return len(filter(condition, (rand.int(sides) for _ in range(dice))))
所有这些方式都具有与使用循环相同的内存/速度特性(即相同的 big-O 特性),尽管使用循环通常会更快一些,因为开销较小。
编辑:这个答案更多地适用于您编辑之前的问题版本,但这个想法与您的新版本非常相似。
编辑2:原标题是“你如何在不使用循环的情况下重写这个问题”,这就是我的回答所适用的,如何在不使用循环的情况下做到这一点。我没有意识到你想要一个数学公式,为什么我的答案根本不适用。