Use Kotlin to implement Y-Combinator (Y-Combinator)

Use Kotlin to implement Y-Combinator (Y-Combinator)

Can we use Kotlin FP (Lambda, function) to write a Y-combinator function?

Y = λf.(λx.f (x x)) (λx.f (x x))

We know that In JS:

function Y(f) {
    
    
    return (function (g) {
    
    
        return g(g);
    })(function (g) {
    
    
        return f(function (x) {
    
    
            return g(g)(x);
        });
    });
}

var fact = Y(function (rec) {
    
    
    return function (n) {
    
    
        return n == 0 ? 1 : n * rec(n - 1);
    };
});

In Coffee:

coffee> Y = (f) -> ((x) -> (x x)) ((x) -> (f ((y) -> ((x x) y))))
[Function]

coffee> fact = Y (f)  ->(n) -> if n==0 then 1 else n*f(n-1)
[Function]

coffee> fact(10)
3628800

In a dynamically typed language, Y-Combinator is easy to implement.

Using Java's interface and type conversion we can also achieve

public class YCombinator {
    
    

    public static Lambda<Lambda> yCombinator2(final Lambda<Lambda> f) {
    
    
        return ((Lambda<Lambda>)(Object input) -> {
    
    
            final Lambda<Lambda> u = (Lambda<Lambda>)input;
            return u.call(u);
        }).call(
            ((Lambda<Lambda>)(Object input) -> {
    
    
                final Lambda<Lambda> v = (Lambda<Lambda>)input;
                return f.call((Lambda<Object>)(Object p) -> {
    
    
                    return v.call(v).call(p);
                });
            })
        );

    }

    public static void main(String[] args) {
    
    
        Lambda<Lambda> y = yCombinator(
            (Lambda<Lambda>)(Object input) -> {
    
    
                Lambda<Integer> fab = (Lambda<Integer>)input;
                return (Lambda<Integer>)(Object p) -> {
    
    
                    Integer n = Integer.parseInt(p.toString());
                    if (n < 2) {
    
    
                        return Integer.valueOf(1);
                    } else {
    
    
                        return n * fab.call(n - 1);
                    }
                };
            });

        System.out.println(y2.call(10));//输出: 3628800
    }

    interface Lambda<E> {
    
    
        E call(Object input);
    }

}

A similar implementation of the Y combinator using Kotlin's OOP programming paradigm is:

object YCombinatorKt {
    
    

    fun yCombinator(f: Lambda<Lambda<*>>): Lambda<Lambda<*>> {
    
    

        return object : Lambda<Lambda<*>> {
    
    

            override fun call(n: Any): Lambda<*> {
    
    
                val u = n as Lambda<Lambda<*>>
                return u.call(u)
            }
        }.call(object : Lambda<Lambda<*>> {
    
    

            override fun call(n: Any): Lambda<*> {
    
    
                val x = n as Lambda<Lambda<*>>

                return f.call(object : Lambda<Any> {
    
    
                    override fun call(n: Any): Any {
    
    
                        return x.call(x).call(n)!!
                    }
                })
            }

        }) as Lambda<Lambda<*>>
    }

    @JvmStatic fun main(args: Array<String>) {
    
    

        val y = yCombinator(object : Lambda<Lambda<*>> {
    
    

            override fun call(n: Any): Lambda<*> {
    
    
                val fab = n as Lambda<Int>

                return object : Lambda<Int> {
    
    

                    override fun call(n: Any): Int {
    
    
                        val n = Integer.parseInt(n.toString())
                        if (n < 2) {
    
    
                            return Integer.valueOf(1)
                        } else {
    
    
                            return n * fab.call(n - 1)
                        }
                    }
                }
            }
        })

        println(y.call(10)) //输出: 3628800
    }

    interface Lambda<E> {
    
    
        fun call(n: Any): E
    }
}

Of course, Kotlin, which also fully supports functional programming, also has an FP-style Y combinator implementation:


/**
 * Created by jack on 2017/7/9.
 *
 * lambda f. (lambda x. (f(x x)) lambda x. (f(x x)))
 *
 * FP YCombinator
 */

// 为了方便易懂,使用 X 用做函数 (X)->Int 的别名
typealias X = (Int) -> Int
// G 递归引用 G 自己
interface G : Function1<G, X>
// create a fun G from lazy blocking
fun G(block: (G) -> X) = object : G {
    
    
    // 调用函数自身 `block(g)` like as `g(g)`
    override fun invoke(g: G) = block(g)
}

typealias F = Function1<X, X>
fun Y(f: F) = (fun(g: G) = g(g))(G {
    
     g -> f({
    
     x -> g(g)(x) }) })

val fact = Y {
    
    
    rec ->
    {
    
    
        n ->
        if (n == 0) 1 else n * rec(n - 1)
    }
}
val fib = Y {
    
     f ->
    {
    
    
        n ->
        if (n == 1 || n == 2) 1 else f(n - 1) + f(n - 2)

    }
}


fun main(args: Array<String>) {
    
    
    println(fact(10))
    println(fib(10))

    for (i in 1..10) {
    
    
        println("$i!= ${
      
      fact(i)}")
    }

    for (i in 1..10) {
    
    
        println("fib($i) = ${
      
      fib(i)}")
    }
}


Among them, the interface G inherits the Function1<G, X> interface:

interface G : Function1<G, X>

The Function1 interface is inherited from the kotlin.Function interface:

public interface Function<out R>

Function1 has an abstract operator function invoke, which is used to call the input parameter p1:

public interface Function1<in P1, out R> : kotlin.Function<R> {
    public abstract operator fun invoke(p1: P1): R
}

For the G function we defined, the input parameter is the block function (G) -> X, and the input parameter of the block function is the G type. The invoke function is used to call itself:

fun G(block: (G) -> X) = object : G {
    // 调用函数自身 `block(g)` like as `g(g)`
    override fun invoke(g: G) = block(g)
}

In this way, we can implement a Y combinator function:

typealias F = Function1<X, X>

fun Y(f: F) = (fun(g: G) = g(g))(G { g -> f({ x -> g(g)(x) }) })

We define the factorial recursive function and Fibonacci sequence function through the Y combinator:

val fact = Y {
    rec ->
    {
        n ->
        if (n == 0) 1 else n * rec(n - 1)
    }
}
val fib = Y { f ->
    {
        n ->
        if (n == 1 || n == 2) 1 else f(n - 1) + f(n - 2)

    }
}

Test code:

fun main(args: Array<String>) {
    val square: X = { x -> x * x }
    println(square(9))

    println(fact(10))
    println(fib(10))

    for (i in 1..10) {
        println("$i!= ${fact(i)}")
    }

    for (i in 1..10) {
        println("fib($i) = ${fib(i)}")
    }
}

Github source code project: https://github.com/EasyKotlin/chapter8_fp


Kotlin Developer Community

Focus on sharing Java, Kotlin, Spring/Spring Boot, MySQL, redis, neo4j, NoSQL, Android, JavaScript, React, Node, functional programming, programming ideas, "high availability, high performance, high real-time" large-scale distributed system architecture design theme.

High availability, high performance, high real-time large-scale distributed system architecture design

Distributed framework: Zookeeper, distributed middleware framework, etc.
Distributed storage: GridFS, FastDFS, TFS, MemCache, redis, etc.
Distributed database: Cobar, tddl, Amoeba, Mycat
cloud computing, big data, AI algorithm
virtualization, cloud native Technology
Distributed computing framework: MapReduce, Hadoop, Storm, Flink, etc.
Distributed communication mechanism: Dubbo, RPC calls, shared remote data, message queues, etc.
Message queue MQ: Kafka, MetaQ, RocketMQ
how to build a highly available system: based on hardware, software Realization of some typical solutions such as middleware and system architecture: HAProxy, Corosync+Pacemaker-based high-availability cluster suite middleware system
Mycat architecture distributed evolution
The problems behind big data Join: contradictions and reconciliation of data, network, memory and computing capabilities
High-performance problems in Java distributed systems: AIO, NIO, Netty or self-developed frameworks?
High-performance event dispatch mechanism: thread pool model, Disruptor model, etc. . .

The embracing wood is born at the end of the mill; the nine-story platform starts from the basement; the journey of a thousand miles begins with a single step. If you don't accumulate steps, you can't reach a thousand miles; if you don't accumulate small streams, you can't become a river.

Introduction to Kotlin

Kotlin is a non-research language. It is a very pragmatic industrial-grade programming language. Its mission is to help programmers solve problems in actual engineering practice. Using Kotlin makes the lives of Java programmers better. The null pointer errors in Java, the lengthy boilerplate code that wastes time, the verbose syntax restrictions, etc., all disappear in Kotlin. Kotlin is simple and pragmatic, with concise and powerful syntax, safe and expressive, and extremely productive.

Java was born in 1995 and has a history of 23 years. The current latest version is Java 9. In the process of continuous development and prosperity of the JVM ecosystem, brother languages ​​such as Scala, Groovy, and Clojure were also born.

Kotlin is also an excellent member of the JVM family. Kotlin is a modern language (version 1.0 was released in February 2016). Its original purpose is to optimize the defects of the Java language like Scala, provide simpler and more practical programming language features, and solve performance problems, such as compilation time. JetBrains has done a great job in these areas.

Features of Kotlin language

After developing in Java for many years, it's great to be able to try something new. If you are a Java developer, Kotlin will be very natural and smooth. If you are a Swift developer, you will feel familiar, such as Nullability. The features of Kotlin language are:

1. Concise

Significantly reduce the amount of boilerplate code.

2. 100% interoperability with Java

Kotlin can directly interact with Java classes and vice versa. This feature allows us to directly reuse our code base and migrate it to Kotlin. Because Java's interoperability is almost everywhere. We can directly access platform APIs and existing code bases, while still enjoying and using all the powerful modern language features of Kotlin.

3. Extension function

Kotlin is similar to C# and Gosu, it provides the ability to provide new functional extensions for existing classes without having to inherit from the class or use any type of design patterns (such as decorator patterns).

4. Functional programming

The Kotlin language first supports functional programming, just like Scala. It has basic functional features such as high-order functions and lambda expressions.

5. Default and named parameters

In Kotlin, you can set a default value for the parameters in a function and give each parameter a name. This helps to write easy-to-read code.

6. Powerful development tool support

And because it is produced by JetBrains, we have great IDE support. Although the automatic conversion from Java to Kotlin is not 100% OK, it is indeed a very good tool. When using IDEA's tools to convert Java code to Kotlin code, 60%-70% of the resulting code can be reused easily, and the cost of modification is small.

In addition to its concise and powerful syntax features, Kotlin also has a very practical API and an ecosystem built around it. For example: collection API, IO extension, reflection API, etc. At the same time, the Kotlin community also provides a wealth of documents and a lot of learning materials, as well as online REPL.

A modern programming language that makes developers happier. Open source forever

Picture from "Kotlin from entry to advanced combat" (Chen Guangjian, Tsinghua University Press)

Picture from "Kotlin from entry to advanced combat" (Chen Guangjian, Tsinghua University Press)

https://kotlinlang.org/

Guess you like

Origin blog.csdn.net/universsky2015/article/details/108669514