Advent of Code Day 1要求以一种或多种形式循环使用((((())(())(((()))((
等长串括号。我们的想法是(
上升到"地板&#34 ;,)
落在一层,目标是打印
使用for循环的命令式解决方案很简单(以Python为例):
def main():
flr = 0
basement = False
for idx, elt in enumerate(text):
flr += {
"(": 1,
")": -1
}.get(elt)
if flr < 0 and not basement:
print("first basement pos:", idx + 1)
basement = True
print("final floor:", flr)
递归函数解决方案有点复杂,但仍然不太难。
def worker(flr, txt, idx, basement):
flr += {"(": 1, ")": -1}[ txt[0] ]
if not (len(txt) - 1): return flr
if flr < 0 and not basement:
print("first basement floor index: ", idx + 1)
basement = True
return worker(flr, txt[1:], idx + 1, basement)
def starter(txt):
flr, basement, idx = 0, False, 0
return worker(flr, txt, idx, basement)
if __name__ == '__main__':
__import__("sys").setrecursionlimit(int(1e5))
print("final floor:", starter(text))
这两个都给出正确的
输出first basement floor index: 1795
final floor: 74
在我的挑战输入时运行。
除了第二个是愚蠢的,因为Python没有尾调用优化,但没关系
如何在Factor中实现这些中的任何一个?自从我开始使用因子以来,我就一直感到困惑。
我们不能只使用for循环,因为没有等价物允许我们在迭代之间保持可变状态。
我们可以使用递归解决方案:
: day-1-starter ( string -- final-floor )
[ 0 ] dip 0 f day-1-worker 3drop "final floor: %s" printf ;
: day-1-worker
( floor string index basement? -- floor string index basement? )
day-1-worker ! what goes here?
; recursive
太好了,这是一个骷髅,但day-1-worker
的内容是什么?因素并没有任何方法可以及早回归&#34;来自递归调用,因为没有办法反向运行程序而没有返回的概念 - 这没有任何意义。
我感觉可能递归并不是因子中这个问题的答案。如果是,我该如何停止递归呢?
答案 0 :(得分:3)
首先,递归总是答案:) 由于这是一个挑战(而且我不知道因素),只需提示: 在您的python解决方案中,您已使用副作用来打印第一个地下室级别。完全没必要!您也可以使用 basemet 参数来保存楼层编号,如下所示:
def worker(flr, txt, idx, basement):
flr += {"(": 1, ")": -1}[ txt[0] ]
if not (len(txt) - 1): return [flr, basement] # <- return both
if flr < 0 and not basement:
#print("first basement floor index: ", idx + 1) # side effects go away!
basement = idx+1 # <- a number in not False, so that's all
return worker(flr, txt[1:], idx + 1, basement)
现在你得到了
final,first_basement = worker(0, txt, 0, False)
或者,或者你也可以编写2个函数,第一个是寻找地下一层的索引,另一个是计算最后一层。即使你关心性能,有+ 2000额外的小步也不是什么大问题。
祝你好运!
修改:截至关于因子递归的问题,请查看the Ackermann Function in Factor和the Fibonacci sequence in Factor,您应该知道如何打破环路&#34 ;.实际上唯一的问题是思考(从命令式模型中解放自己:));在函数式语言中,没有&#34; return&#34;,只是最终的值,你提到的基于堆栈的语言是同一事物的其他计算模型(而不是考虑折叠一个人想到的树#34;推送和弹出堆栈&#34; - 这是实现前者的常用方法。
编辑:( SPOILER!) 我安装了Factor并开始玩它(相当不错),对于第一个问题(计算最终得分)可能的解决方案是
: day-1-worker ( string floor -- floor )
dup length 0 =
[ drop ]
[ dup first 40 =
[ swap 1 + ]
[ swap 1 - ]
if
swap rest
day-1-worker ]
if ;
: day-1-starter ( string -- floor )
0 swap day-1-worker ;
所以现在你可以为计算地下室的索引写一个类似的,或者(这会更酷!)来修改它,以便它也管理索引和基础...(可能使用 cond 比嵌套 if s更明智。
答案 1 :(得分:3)
您可以使用cum-sum
组合器:
: to-ups/downs ( str -- seq )
[ CHAR: ( = 1 -1 ? ] { } map-as ;
: run-elevator ( str -- first-basement final-floor )
to-ups/downs cum-sum [ -1 swap index 1 + ] [ last ] bi ;
IN: scratchpad "((())))(())(())(((()))((" run-elevator
--- Data stack:
7
2
答案 2 :(得分:2)
修改
我最初误读了你计算basement
值的方式。我已经更新了以下答案
这是一个JavaScript解决方案。对不起,我不知道这如何转换为因子。 reduce
是一个迭代过程
const worker = txt=>
txt.split('').reduce(({floor, basement}, x, i)=> {
if (x === '(')
return {floor: floor + 1, basement}
else if (basement === null && floor === 0)
return {floor: floor - 1, basement: i}
else
return {floor: floor - 1, basement}
}, {floor: 0, basement: null})
let {floor, basement} = worker('((((())(())(((()))((')
console.log(floor) //=> 6
console.log(basement) //=> null; never reaches basement
上面的答案取决于某些.split
和.reduce
,这些可能不会出现在您的语言中。这是使用Y-combinator的另一种解决方案,只有substring
内置(大多数语言都包含在内)。这个答案还取决于你的语言是否具有一流的功能。
const U = f=> f (f)
const Y = U (h=> f=> f (x=> h (h) (f) (x)))
const strhead = s=> s.substring(0,1)
const strtail = s=> s.substring(1)
const worker = Y (f=> ({floor, basement})=> i=> txt=> {
// txt is empty string; return answer
if (txt === '')
return {floor, basement}
// first char in txt is '(', increment the floor
else if (strhead (txt) === '(')
return f ({floor: floor + 1, basement}) (i+1) (strtail (txt))
// if basement isn't set and we're on floor 0, we found the basement
else if (basement === null && floor === 0)
return f ({floor: floor - 1, basement: i}) (i+1) (strtail (txt))
// we're already in the basement, go down another floor
else
return f ({floor: floor - 1, basement}) (i+1) (strtail (txt))
}) ({floor: 0, basement: null}) (0)
{
let {floor, basement} = worker('((((())(())(((()))((')
console.log(floor) //=> 6
console.log(basement) //=> null; never reaches basement
}
{
let {floor, basement} = worker(')(((((')
console.log(floor) //=> 4
console.log(basement) //=> 0
}
{
let {floor, basement} = worker('((())))')
console.log(floor) //=> -1
console.log(basement) //=> 6
}