стиль передачи продолжения
Стиль передачи продолжения эффективно обеспечивает программирование c return
. Использование рекурсивной функции CPS может привести к тому, что сложность программы превратится в воздух -
const identity = x =>
x
const sumfib = (n = 0, then = identity) =>
n <= 0
? then(0, 1, 1) // base case
: sumfib // inductive: solve smaller subproblem
( n - 1
, (sum, fib, temp) =>
then(sum + fib, temp, fib + temp)
)
console.log
( sumfib(0) // 0 = 0
, sumfib(1) // 1 = 0 + 1
, sumfib(2) // 2 = 0 + 1 + 1
, sumfib(3) // 4 = 0 + 1 + 1 + 2
, sumfib(4) // 7 = 0 + 1 + 1 + 2 + 3
, sumfib(5) // 12 = 0 + 1 + 1 + 2 + 3 + 5
, sumfib(6) // 20 = 0 + 1 + 1 + 2 + 3 + 5 + 8
, sumfib(7) // 33 = 0 + 1 + 1 + 2 + 3 + 5 + 8 + 13
)
loop / recur
loop
и recur
дают нам возможность писать рекурсивные программы, такие как один выше, но не встретит ошибку переполнения стека -
const recur = (...values) =>
({ recur, values })
const loop = f =>
{ let r = f()
while (r && r.recur === recur)
r = f(...r.values)
return r
}
const sumfib = (n = 0) =>
loop // <-- loop with vars
( ( m = n
, sum = 0
, fib = 1
, temp = 1
) =>
m <= 0 // <-- exit condition
? sum // <-- base case
: recur // <-- recur with updated vars
( m - 1
, sum + fib
, temp
, temp + fib
)
)
console.log
( sumfib(0) // 0 = 0
, sumfib(1) // 1 = 0 + 1
, sumfib(2) // 2 = 0 + 1 + 1
, sumfib(3) // 4 = 0 + 1 + 1 + 2
, sumfib(4) // 7 = 0 + 1 + 1 + 2 + 3
, sumfib(5) // 12 = 0 + 1 + 1 + 2 + 3 + 5
, sumfib(6) // 20 = 0 + 1 + 1 + 2 + 3 + 5 + 8
, sumfib(7) // 33 = 0 + 1 + 1 + 2 + 3 + 5 + 8 + 13
)
streamz
так называемые streams интересны тем, что могут генерировать бесконечные значения , но нам не нужно вычислять их все сразу. Опять же, мы можем определить нашу программу в простых терминах и позволить полезным примитивам выполнить всю тяжелую работу -
const fibs =
stream(0, _ =>
stream(1, _ =>
streamAdd(fibs, fibs.next)))
console.log(streamTake(fibs, 10))
// [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ]
console.log(streamTake(streamSum(fibs), 10))
// [ 0, 1, 2, 4, 7, 12, 20, 33, 54, 88 ]
Мы просто реализуем stream
, streamAdd
, streamSum
и streamTake
-
const emptyStream =
Symbol('emptyStream')
const stream = (value, next) =>
( { value
, get next ()
{ delete this.next
return this.next = next()
}
}
)
const streamAdd = (s1, s2) =>
s1 === emptyStream || s2 === emptyStream
? emptyStream
: stream
( s1.value + s2.value
, _ => streamAdd(s1.next, s2.next)
)
const streamSum = (s, sum = 0) =>
s === emptyStream
? emptyStream
: stream
( sum + s.value
, _ => streamSum(s.next, sum + s.value)
)
const streamTake = (s = emptyStream, n = 0) =>
s === emptyStream || n <= 0
? []
: [ s.value, ...streamTake(s.next, n - 1) ]
Разверните фрагмент ниже, чтобы проверить результаты в своем собственном браузере -
const emptyStream =
Symbol('emptyStream')
const stream = (value, next) =>
( { value
, get next ()
{ delete this.next
return this.next = next()
}
}
)
const streamAdd = (s1, s2) =>
s1 === emptyStream || s2 === emptyStream
? emptyStream
: stream
( s1.value + s2.value
, _ => streamAdd(s1.next, s2.next)
)
const streamSum = (s, sum = 0) =>
s === emptyStream
? emptyStream
: stream
( sum + s.value
, _ => streamSum(s.next, sum + s.value)
)
const streamTake = (s = emptyStream, n = 0) =>
s === emptyStream || n <= 0
? []
: [ s.value, ...streamTake(s.next, n - 1) ]
const fibs =
stream(0, _ =>
stream(1, _ =>
streamAdd(fibs, fibs.next)))
console.log(streamTake(fibs, 10))
// [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ]
console.log(streamTake(streamSum(fibs), 10))
// [ 0, 1, 2, 4, 7, 12, 20, 33, 54, 88 ]