简体   繁体   English

javascript中K-combinator(Kestrel)的实际使用

[英]Practical use of K-combinator (Kestrel) in javascript

The K-combinator can be implemented as below and the implementation should not have any side-effects. K-combinator 可以如下实现,并且该实现应该没有任何副作用。

const K = x => y => x;

It is sometimes called "const" (as in Haskell).它有时被称为“const”(如在 Haskell 中)。 The K function might be defined as, "takes a value and returns a (constant) unary function that always returns that value." K 函数可能被定义为“接受一个值并返回一个始终返回该值的(常量)一元函数。”

When is it useful?什么时候有用? Please help me with practical examples.请帮助我提供实际示例。

Sort of an broad question, but it's nice, I like it. 一个广泛的问题,但它很好,我喜欢它。

To support my example, in this answer I'm going to implement … 为了支持我的例子,我将在这个答案中实施......

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

… which as the type suggests, takes a number and a function to build an array. ...根据类型的建议,需要一个数字和一个函数来构建一个数组。 This could be useful if you want to build an array of a known size based on some computation. 如果要根据某些计算构建已知大小的数组,这可能很有用。


Let's build an array with 5 elements using the identity function, id . 让我们使用身份函数id构建一个包含5个元素的数组。 As you can see, a sequential numerical index starting with 0 is given to your builder function 如您所见,为构建函数提供了以0开头的顺序数字索引

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

Let's do something mathy with the builder this time. 让我们这次与建造者做些蠢事。 We'll square the input. 我们将对输入进行平方。 Very advanced. 非常先进。

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

Or maybe we don't care about the input. 或者也许我们不关心输入。 I always love a good laugh. 我总是喜欢开怀大笑。 I laugh at things constantly. 我经常嘲笑事物。 One could say I K('ha') 有人可以说我K('ha') ......

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

Boom ! 轰! Pretty useful, right? 非常有用,对吗? That's K 那是K


Implementation 履行

Go ahead and run it to see K in action! 继续运行它,看看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'] 

The problem with K as with all primitive combinators is that you can't consider it on its own. 与所有原始组合子一样, K的问题在于你不能单独考虑它。 Primitive combinators are the fundamental building blocks of functional programming. 原始组合器是函数式编程的基本构建块。 You need a proper context to watch them at work. 你需要一个适当的环境来观察他们的工作。 The challenge is to grok this context, if you are new to the functional paradigm. 如果您不熟悉功能范例,那么挑战在于了解这一背景。

Here is a "typical context": Option . 这是一个“典型背景”: Option Instances of the Option type are like values that may be null , but never throw an error when applied to a function: 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](ox), 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); 

What does K do in this implementation? K在这个实现中做了什么? Let's examine the crucial line: 让我们来看看关键线:

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

Option.fold expects two functions. Option.fold需要两个函数。 The first passed function x => Option.of(f(x)) is for the some case (there is a value). 第一个传递函数x => Option.of(f(x))用于some情况(有一个值)。 The second K(o) for the none case (there is no value). none例子的第二个K(o) (没有值)。 Let's recall that K expects two argument K = x => y => {return x} . 让我们回想一下, K期望两个参数K = x => y => {return x} K(o) assigns o to x . K(o)o赋给x No matter what is passed as second argument, K will always ignore y and return x instead. 无论作为第二个参数传递什么, K将始终忽略y并返回x

But what does o represent in the expression K(o) ? o在表达式K(o)代表什么? It represents Option.none that is, the absence of a value. 它代表Option.none ,即没有值。 So when someone tries to map a function f over none , none is just returned, no matter what f passes as second argument to K . 因此,当有人试图将函数f映射为nonenone返回任何函数,无论f作为第二个参数传递给K

K combinator can be also used as truth-value, when using church-encoded booleans. 当使用教会编码的布尔值时,K组合子也可以用作真值。 Ie IF-TEST THEN ELSE: if your "IF-TEST" returns K, then "else" would be dropped and "then" would be executed. 即IF-TEST那么:如果你的“IF-TEST”返回K,那么“else”将被删除并且“then”将被执行。

Suppose you have an array of numbers xs :假设您有一个数字数组xs

[10, 5, 12, 7]

You need to transform it in two ways:您需要通过两种方式对其进行转换:

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]

I'll quickly acknowledge that the ternary expressions are probably just fine but for the purpose of answering the question I'll make them :我很快就会承认三元表达式可能很好,但为了回答这个问题,我会让它们

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]

Now let's say that we just want to return 'even' or 'odd' :现在假设我们只想返回'even''odd'

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

The two lambdas behave in the same way: they completely ignore x and always return the same value.这两个 lambda 表达式的行为方式相同:它们完全忽略x始终返回相同的值。 This is exactly what the K estrel combinator is about:这正是K estrel 组合器的意义所在:

const K = a => x => a;

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM