【问题标题】:Practical use of K-combinator (Kestrel) in javascriptjavascript中K-combinator(Kestrel)的实际使用
【发布时间】:2016-12-02 19:14:04
【问题描述】:

K-combinator 可以如下实现,并且该实现不应该有任何副作用。

const K = x => y => x;

它有时被称为“const”(如在 Haskell 中)。 K 函数可以定义为,“接受一个值并返回一个(常量)一元函数,该函数总是返回该值。”

什么时候有用?请帮我举一些实际的例子。

【问题讨论】:

标签: javascript functional-programming combinators


【解决方案1】:

有点宽泛的问题,但很好,我喜欢。

为了支持我的例子,在这个答案中,我将实现......

abuild :: Number -> (Number -> a) -> [a]

... 正如类型所暗示的那样,它需要一个数字和一个函数来构建一个数组。如果您想根据一些计算构建一个已知大小的数组,这可能会很有用。


让我们使用标识函数id 构建一个包含 5 个元素的数组。如您所见,为您的 builder 函数提供了一个从 0 开始的顺序数字索引

abuild (5) (id) // => [0,1,2,3,4]

这次让我们和建造者一起做点数学题吧。我们将平方输入。非常先进。

abuild (5) (x=> x * x)
// => [0,1,4,9,16]

或者也许我们不关心输入。我总是喜欢开怀大笑。我经常对事情发笑。可以说我K('ha') ...

abuild (5) (K('ha'))
// => ['ha','ha','ha','ha','ha']

轰隆隆!很有用,对吧?那是K


实施

继续运行它以查看K 的实际效果!

// id :: a -> a
const id = x=> x

// K :: a -> b -> a
const K = x=> y=> x

// add :: Number -> Number -> Number
const add = x=> y=> y + x

// reduce :: (a -> b) -> b -> [a] -> b 
const reduce = f=> y=> ([x,...xs])=> {
  if (x === undefined)
    return y
  else
    return reduce (f) (f (y) (x)) (xs)
}

// map :: (a -> b) -> [a] -> [b]
const map = f=> reduce (xs=> x=> [...xs, f(x)]) ([])

// iterate :: Number -> (a -> a) -> a -> [a]
const iterate = n=> f=> x=>
  n > 0 ? [x, ...iterate (n - 1) (f) (f(x))] : []

// abuild :: Number -> (Number -> a) -> [a]
const abuild = n=> f=>
  map (f) (iterate (n) (add (1)) (0))

console.log(abuild (5) (id))
// => [0,1,2,3,4]

console.log(abuild (5) (x=> x * x))
// => [0,1,4,9,16]

console.log(abuild (5) (K('ha')))
// => ['ha','ha','ha','ha','ha']

【讨论】:

  • 我喜欢幽默的功能 :D K("ha") 是在和某人一起笑吗?!
  • 这只是对我的一点见解!我喜欢轻视任何情况^_^
  • 但是生成["ha", "ha", "ha"] 有什么用呢?
【解决方案2】:

K 的问题与所有原始组合符一样,是您不能单独考虑它。原始组合器是函数式编程的基本构建块。你需要一个适当的环境来观察他们的工作。如果您是函数式范式的新手,那么您面临的挑战是深入了解这个背景。

这是一个“典型上下文”:OptionOption 类型的实例类似于可能是 null 的值,但在应用于函数时不会抛出错误:

// the option type

const Option = {
  some: Symbol.for("ftor/Option.some"),

  none: Symbol.for("ftor/Option.none"),

  of: x => factory(Option.some) (x),

  cata: pattern => o => pattern[o.tag](o.x),

  fold: f => g => o => Option.cata({[Option.some]: f, [Option.none]: g}) (o),

  map: f => o => Option.fold(x => Option.of(f(x))) (K(o)) (o)
  //                                                ^^^^
}


// a generic map function

const map = type => f => o => type.map(f) (o);


// functor factory

const factory = tag => value => (
  {x: value === undefined ? null : value, tag: tag}
);


// auxiliary functions

const K = x => y => x;
const sqr = x => x * x;


// a few data to play around

const o = factory(Option.some) (5);
const p = factory(Option.none) ();



// and run

let r1 = map(Option) (sqr) (o);
let r2 = map(Option) (sqr) (p);

console.log("map over o", r1);
console.log("map over p", r2);

K 在这个实现中做了什么?让我们检查一下关键线:

f => o => Option.fold(x => Option.of(f(x))) (K(o)) (o)

Option.fold 需要两个函数。第一个传递的函数x => Option.of(f(x)) 是针对some 的情况(有一个值)。第二个K(o) 用于none 案例(没有值)。让我们回想一下K 需要两个参数K = x => y => {return x}K(o)o 分配给 x。无论作为第二个参数传递什么,K 将始终忽略 y 并返回 x

但是o 在表达式K(o) 中代表什么?它表示Option.none,即没有值。因此,当有人尝试将函数 f 映射到 none 上时,none 将被返回,无论 f 作为第二个参数传递给 K

【讨论】:

  • 前置条件函数“precon”在哪里?
【解决方案3】:

当使用教堂编码的布尔值时,K 组合子也可以用作真值。 IE。 IF-TEST THEN ELSE:如果您的“IF-TEST”返回 K,则“else”将被删除,“then”将被执行。

【讨论】:

    【解决方案4】:

    假设你有一个数字数组xs

    [10, 5, 12, 7]
    

    你需要通过两种方式对其进行改造:

    xs.map(x => x % 2 == 0 ? x + 1 : x + 2);
    //=> [11, 7, 13, 9]
    
    xs.map(x => x % 2 == 0 ? x + 10 : x + 20);
    //=> [20, 25, 22, 27]
    

    我很快就会承认三元表达式可能还不错,但为了回答这个问题,我将它们设为

    const ifelse = (p, t, f) => x => p(x) ? t(x) : f(x);
    const add = a => x => a + x;
    const even = x => x % 2 == 0;
    
    xs.map(ifelse(even, add(1), add(2)));
    //=> [11, 7, 13, 9]
    
    xs.map(ifelse(even, add(10), add(20)));
    //=> [20, 25, 22, 27]
    

    现在假设我们只想返回'even''odd'

    xs.map(ifelse(even, () => 'even', () => 'odd'));
    //=> ['even', 'odd', 'even', 'odd']
    

    两个 lambda 的行为方式相同:它们完全忽略 x 并且 总是 返回相同的值。这正是 Kestrel 组合器的意义所在:

    const K = a => x => a;
    
    xs.map(ifelse(even, K('even'), K('odd')));
    //=> ['even', 'odd', 'even', 'odd']
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-28
      • 2017-07-30
      • 1970-01-01
      • 2020-04-03
      • 2011-10-22
      相关资源
      最近更新 更多