Получение зацикленных входов и выходов

Представьте себе, как я пытаюсь создать программу, которая будет вечно вычислять числа Фибоначчи в последовательном порядке. Я хочу, чтобы команда выдавала выходные данные и обрабатывала выходные данные как входные, выполняя над ними некоторую операцию. Как мне написать такую ​​программу на Python и как мне заставить ее остановиться, если она не будет автоматически останавливаться после определенного момента?

Поэтому я попробовал ввести значения вручную, но не знаю, как сделать это автоматически, поскольку я новичок.

Привет! Возможно, вас заинтересуют генераторы . Напишите свою функцию Фибоначчи, используя ключевое слово yield вместо return, как бесконечный цикл, который дает fib0, затем дает fib1, затем дает fib2 и т. д.

Stef 08.07.2024 13:55
Учебная записка [Medium] Leetcode#22 Generate Parentheses
Учебная записка [Medium] Leetcode#22 Generate Parentheses
На этот раз мы собираемся решить еще одну классическую проблему, связанную с парными скобками, так называемую генерацию скобок.
1
1
51
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Предлагаю взглянуть на функции генератора.

В качестве примера, вот функция-генератор, которая постоянно выдает четные числа:

def gen_even():
    k = 0
    while True:
        yield k
        k += 2

Затем вы можете использовать эту функцию, чтобы получить несколько четных чисел и выполнить над ними операции:

for i, even_number in zip(range(10), gen_even()):
    square = even_number**2
    print(f'The {i}th even number is {even_number} and its square is {square}.')

Выход:

The 0th even number is 0 and its square is 0.
The 1th even number is 2 and its square is 4.
The 2th even number is 4 and its square is 16.
The 3th even number is 6 and its square is 36.
The 4th even number is 8 and its square is 64.
The 5th even number is 10 and its square is 100.
The 6th even number is 12 and its square is 144.
The 7th even number is 14 and its square is 196.
The 8th even number is 16 and its square is 256.
The 9th even number is 18 and its square is 324.

Альтернативно вы можете назначить генератор переменной и извлекать из него следующие числа, используя next:

g = gen_even()
for i in range(5):
    even_number = next(g)
    # some operations...
Ответ принят как подходящий

вычислять числа Фибоначчи в последовательном порядке...

Итак, у вас будет функция для этого расчета. Генератор может быть выбором:

def gen_fib():
    a, b = 0, 1  # Let's start the sequence with 1 (not 0)
    while True:
        yield b
        a, b = b, a+b

...навсегда.

Это предполагает, что вы просто продолжите перебирать итератор с помощью цикла for:

for a in gen_fib():
    # ...

Я хочу, чтобы команда выдавала вывод...

for a in gen_fib():
    print(a)  # output

... и рассматривать выходные данные как входные, выполняя над ними некоторую операцию.

prev = 2
for a in gen_fib():
    print(a)  # output
    ratio = a / prev  # calculation
    prev = a

как мне заставить его остановиться, если он не остановится автоматически после определенного момента?

Вам решать, при каком условии программа должна остановиться. Это может быть пользователь, просто завершивший программу (тогда делать нечего), или какое-то удовлетворительное состояние, которое было достигнуто. Например, возможно, вы хотите напечатать соотношение двух последовательных чисел Фибоначчи, которое, как известно, сходится, и выйти, когда это рассчитанное соотношение больше не изменится:

def gen_fib():
    a, b = 0, 1  # Let's start the sequence with 1 (not 0)
    while True:
        yield b
        a, b = b, a+b


prev, prev_ratio = 2, 2
for a in gen_fib():
    ratio = a / prev   # calculation
    print(a, ratio)  # output
    if abs(ratio - prev_ratio) < 1e-16:
        break  # That ratio is good enough!
    prev, prev_ratio = a, ratio

с рекурсией

Вы использовали тег recursion, поэтому я предлагаю решение, которое подходит к проблеме рекурсивно —

const fib =
  stream(0, () =>
    stream(1, () =>
      streamAdd(fib, fib.next)))

Интуитивно stream принимает значение и преобразователь, () => ... который вычисляет следующий поток:

const stream = (value, next) => ({
  value,
  get next() {
    delete this.next
    return this.next = next() // evaluates at most once
  }
})

Функция stream создает обычные объекты, что позволяет нам написать streamAdd, который просто складывает два потока вместе. Мы используем emptyStream для завершения конечных потоков —

const emptyStream =
  Symbol('emptyStream')

const streamAdd = (s1, s2) => {
  if (s1 == emptyStream) return emptyStream
  if (s2 == emptyStream) return emptyStream
  return stream(
    s1.value + s2.value,
    () => streamAdd(s1.next, s2.next),
  )
}

Мы можем визуализировать бесконечный поток fib, где ... равен результату streamAdd(fib, fib.next) -

       fib  0 -> 1 -> ...
            +
  fib.next  1 -> ...
            =

streamAdd(
  fib,
  fib.next
)           1
       fib  0 -> 1 -> 1 -> ...
            +    +
  fib.next  1 -> 1 -> ...
            =    =

streamAdd(
  fib,
  fib.next
)           1 -> 2
       fib  0 -> 1 -> 1 -> 2 -> ...
            +    +    +
  fib.next  1 -> 1 -> 2 -> ...
            =    =    =

streamAdd(
  fib,
  fib.next
)           1 -> 2 -> 3
       fib  0 -> 1 -> 1 -> 2 -> 3 -> ...
            +    +    +    +
  fib.next  1 -> 1 -> 2 -> 3 -> ...
            =    =    =    =

streamAdd(
  fib,
  fib.next
)           1 -> 2 -> 3 -> 5
       fib  0 -> 1 -> 1 -> 2 -> 3 -> 5 -> ...
            +    +    +    +    +
  fib.next  1 -> 1 -> 2 -> 3 -> 5 -> ...
            =    =    =    =    =

streamAdd(
  fib,
  fib.next
)           1 -> 2 -> 3 -> 5 -> 8
       fib  0 -> 1 -> 1 -> 2 -> 3 -> 5 -> 8 -> ...
            +    +    +    +    +    +
  fib.next  1 -> 1 -> 2 -> 3 -> 5 -> 8 -> ...
            =    =    =    =    =    =

streamAdd(
  fib,
  fib.next
)           1 -> 2 -> 3 -> 5 -> 8 -> 13

Запустите программу ниже, чтобы увидеть первые 10 чисел Фибоначчи:

const emptyStream =
  Symbol('emptyStream')

const stream = (value, next) => ({
  value,
  get next() {
    delete this.next
    return this.next = next()
  },
})

const streamAdd = (s1, s2) => {
  if (s1 == emptyStream) return emptyStream
  if (s2 == emptyStream) return emptyStream
  return stream(
    s1.value + s2.value,
    () => streamAdd(s1.next, s2.next),
  )
}

const fib =
  stream(0, () =>
    stream(1, () =>
      streamAdd(fib, fib.next)))

for (let s = fib, i = 0; i < 10; s = s.next, i = i + 1)
  console.info(s.value)
.as-console-wrapper { min-height: 100%; top: 0; }

с большей рекурсией

Вероятно, кажется неестественным обрабатывать поток с помощью цикла for. stream первоначально представлен в этих вопросах и ответах, где мы увидим другие функции потока. Обратите внимание на корреляцию между рекурсивной структурой данных и рекурсивной процедурой. Эти два, в частности, полезны для преобразования бесконечного потока в конечный массив:

const take = (s = emptyStream, n = 0) =>
  s === emptyStream || n <= 0
    ? emptyStream
    : stream(s.value, _ => take(s.next, n - 1))
    
const toArray = (s = emptyStream) =>
  s === emptyStream
    ? []
    : [ s.value, ...toArray(s.next) ]

Вот первые 10 элементов потока fib, преобразованные в массив:

toArray(take(fib, 10))
[ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ]

Запустите программу еще раз, чтобы преобразовать поток в массив —

const emptyStream =
  Symbol('emptyStream')

const stream = (value, next) => ({
  value,
  get next() {
    delete this.next
    return this.next = next()
  },
})

const streamAdd = (s1, s2) => {
  if (s1 == emptyStream) return emptyStream
  if (s2 == emptyStream) return emptyStream
  return stream(
    s1.value + s2.value,
    () => streamAdd(s1.next, s2.next),
  )
}

const take = (s = emptyStream, n = 0) =>
  s === emptyStream || n <= 0
    ? emptyStream
    : stream(s.value, _ => take(s.next, n - 1))
    
const toArray = (s = emptyStream) =>
  s === emptyStream
    ? []
    : [ s.value, ...toArray(s.next) ]

const fib =
  stream(0, () =>
    stream(1, () =>
      streamAdd(fib, fib.next)))

console.info(toArray(take(fib, 10)))
.as-console-wrapper { min-height: 100%; top: 0; }

В исходном посте есть версия toArray, которую я бы сегодня писать не стал. Он использует рекурсию и демонстрирует лучшую эргономику, поэтому я решил включить его, однако это крайне неэффективная функция. Урок здесь заключается в том, что, создавая такие абстракции, как stream, мы получаем точки настройки для оптимизации частей нашей программы без необходимости переписывать остальные.

const toArray = (s = emptyStream) => {
  const arr = []
  for (let m = s; m !== emptyStream; m = m.next)
    arr.push(m.value)
  return arr
}

Другие вопросы по теме