Как остановить рекурсию?

появление кода день 1 требует цикла, в той или иной форме, над длинной строкой круглых скобок, таких как ((((())(())(((()))(( etc. Идея в том, что ( поднимается на один "этаж", ) спускается на один этаж, и цели состоят в том, чтобы напечатать

  1. первый индекс в строке, где номер этажа отрицательный и
  2. последний этаж, когда конец строки найден.

императивное решение с циклом 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
}