點燈坊

失くすものさえない今が強くなるチャンスよ

使用 until() 實現 do while

Sam Xiao's Avatar 2020-03-02

有些演算法在 Imperative 需使用 do while 實現,在 Ramda 可改用 until()

Version

macOS Catalina 10.15.3
VS Code 1.42.1
Quokka 1.0.277
Ramda 0.27.0

Imperative

let f = a => b => c => {
  let result = c

  do {
    result = result * b
  } while(result <= a)

  return result
}

f(10)(2)(1) // ?

若我們希望某數值每次 乘以 2,直到 大於 10 才停下來。

Imperative 會使用 do while,直到 while() 內 expression 為 false 為止。

until000

until()

let until = f1 => f2 => z => {
  let result = z

  do {
    result = f2(result)
  } while(!f1(result))

  return result
}

let f = a => b => c => until(x => x > a)(x => x * b)(c)

f(10)(2)(1) // ?

我們可將 do while() 抽成 until() higher-order function,其中前兩個 argument 都是傳入 function。

until001

Ramda

import { until } from 'ramda'

let f = a => b => c => until(x => x > a)(x => x * b)(c)

f(10)(2)(1) // ?

Ramda 已經提供 until() 可直接使用。

until()
(a → Boolean) → (a → a) → a → a
提供 predicate 與 transformation function,直到 predicate 為 true 才停止

a -> Boolean:predicate,描述停止的條件

a -> a:transformation function,描述數值如何改變

a :傳入 data

a:回傳相同型別 data

until002

Point-free

  import { until, gt, multiply, __ } from 'ramda'

  let f = a => b => until(gt(__, a), multiply(b))

  f(10)(2)(1) // ?

可將 f() 最後一個 c argument 拿掉,且 until() 內的 callback 也能以 gt()multiply() 產生,使 callback 也 point-free。

until003

useWith()

import { until, gt, flip, multiply, useWith } from 'ramda'

let f = useWith(until,[flip(gt), multiply])

f(10)(2)(1) // ?

也可進一步使用 useWith() 使 f() 完全 point-free。

until004

Conclusion

  • until() 在實務上較少使用,但若真有 do while 需求,可以改用 until()

Reference

Ramda, until()
Ramda, gt()
Ramda, multiply()
Ramda, __()
Ramda, useWith()
Ramda, flip()