Как перестать повторяться?
Появление первого дня кода требует зацикливания в той или иной форме над длинной строкой скобок, например ((((())(())(((()))((
и т.д. Идея в том, что (
поднимается на один "этаж", )
спускается на один этаж, и цель состоит в том, чтобы напечатать
- первый индекс в строке, где номер этажа является отрицательным и
- последний этаж, когда найден конец строки.
Императивное решение с циклом 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
? Фактор не имеет никакого способа "досрочного возврата" из рекурсивного вызова, потому что нет способа запустить программу в обратном порядке и нет понятия возврата - это не имеет никакого смысла.
У меня такое чувство, что рекурсия не является ответом на этот вопрос в Факторе. Если это так, как я могу прекратить повторение?
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 дополнительных маленьких шагов не имеет большого значения, даже если вы заботитесь о производительности.
Удачи!
Отредактируйте: что касается вашего вопроса о рекурсии в факторе, взгляните на функцию Аккермана в факторе и последовательность Фибоначчи в факторе, и вы должны получить представление о том, как "разорвать петлю". На самом деле единственная проблема в мышлении (освободить себя от императивной модели:)); в функциональных языках нет "возврата", только конечное значение, и языки на основе стека, о которых вы говорите, являются другой вычислительной моделью того же самого (вместо того, чтобы думать о сворачивании дерева, мы думаем о "выталкивании и извлечении из стеков" " - что, кстати, является общим способом реализации первого).
Изменить: (СПОЙЛЕР!) Я установил фактор и начал играть с ним (довольно приятно), для первого вопроса (вычисление окончательного балла) возможное решение
: 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-комбинатора и только 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
}