2016-07-28 54 views
10

K-combinator可以按照下面的方式实现,实现不应该有任何副作用。K-combinator(Kestrel)在javascript中的实际应用

const K = x => y => x; 

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

什么时候有用?请以实际例子帮助我。

+0

http://cs.stackexchange.com/questions/55441/what-is-the-purpose-of-the-ski-combinator-calculusor-even-lambda-calculus- wha –

+0

它看起来像CL函数[constanty](http://clhs.lisp.se/Body/f_cons_1.htm),它有时非常有用。 – Sylwester

+3

在咖喱语言中,只要您希望回调忽略第一个参数,就可以使用它。 – Bergi

回答

2

K与所有原始组合器一样的问题是,您无法单独考虑它。原始组合器是函数式编程的基本组成部分。你需要一个合适的环境来观察他们在工作中。如果你不熟悉功能范例,那么挑战这个背景。

这是一个“典型环境”:Option。在Option类型的实例都是这样,可能是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)分配ox。无论通过第二个参数,K将始终忽略y,并返回x

o代表K(o)表示什么?它代表Option.none即没有价值。因此,当有人试图通过none映射函数f时,仅返回none,无论f作为第二个参数传递给K

+0

预处理函数“precon”在哪里? –

1

当使用教会编码的布尔值时,K combinator也可以用作真值。 也就是说IF-TEST then else:如果你的“IF-TEST”返回K,那么“else”将被丢弃,然后“then”被执行。

+0

任何代码片段? –

2

排序的一个广泛的问题,但它很好,我喜欢它。

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

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

…正如类型所暗示的那样,它需要一个数字和一个函数来构建一个数组。如果你想基于一些计算来构建已知大小的数组,那么这会很有用。


让我们使用标识函数id构建一个包含5个元素的数组。正如可以看到,从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']

+0

我喜欢幽默的功能:D是“K(”哈“)”与某人在一起笑吗?! – ftor

+1

这只是对我的一点洞察!我喜欢轻视任何情况^ _ ^ – naomik