Как остановить рекурсию?
появление кода день 1 требует цикла, в той или иной форме, над длинной строкой круглых скобок, таких как ((((())(())(((()))((
etc. Идея в том, что (
поднимается на один "этаж", )
спускается на один этаж, и цели состоят в том, чтобы напечатать
- первый индекс в строке, где номер этажа отрицательный и
- последний этаж, когда конец строки найден.
императивное решение с циклом 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 не имеет оптимизации хвостового вызова, но не имеет значения, что
как я могу реализовать любой из этих факторов? Это то, что меня смущает с тех пор, как я начал использовать фактор.
мы не можем просто использовать цикл 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
? Фактор не имеет никакого способа "раннего возврата" из рекурсивного вызова, потому что нет способа запустить программу в обратном направлении и нет!--37-->концепция возврата-это не имеет никакого смысла.
Я чувствую, что, возможно, рекурсия не является ответом на этот вопрос в факторе. Если да, то как остановить рекурсию?
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 функции, первая ищет индекс первого подвала этаж, другой просто вычисляет последний этаж. Наличие
удачи!
Edit: что касается вашего вопроса о рекурсии в факторе, взгляните на функция Аккермана в факторе и последовательность Фибоначчи в фактор и вы должны получить представление о том, как "разорвать петлю". На самом деле единственная проблема заключается в мышлении (emancipate себя из императивной модели :)); в функциональных языках нет "возврата", только конечное значение, а языки на основе стека, которые вы упоминаете, являются другой вычислительной моделью того же самого (вместо того, чтобы думать о сворачивании дерева, вы думаете о "толчке и всплывании в/из стеков"-что является обычным способом реализации первого).
Edit: (спойлер!) Я установил фактор и начал играть с ним (довольно приятно), для первого вопроса (вычисление окончательный счет) возможное решение
: 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 было бы мудрее, чем гнездиться еслиs).
можно использовать 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
редактировать
Я изначально неправильно понял, как вы вычисляли 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
}