код Kotlin & Java

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: код Kotlin & Java

Непрочитанное сообщение Olej » 04 июн 2017, 18:04

Здесь уже сразу начинаются интересные вещи:
Olej писал(а):

Код: Выделить всё

    val tests: Array< ()->Unit > = arrayOf(
            { println( tg( 1.0 ) ); },
            { test1() }
    )
Здесь записан массив функций (в данном случае массив размерности 2).
Что есть запись в коде: { ... }?
Это лямбда выражение, запись кода анонимной функции с прототипом fun (Unit): Unit, которое может быть записано в любом месте, где оно и используется.
Olej писал(а):

Код: Выделить всё

fun tg( x: Double ): Double = sin( x ) / cos( x ); // функция - одиночное выражение
А вот так может записываться функция, если она вычисляется как одно, сколь угодно сложное, вычисляемое выражение.

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: код Kotlin & Java

Непрочитанное сообщение Olej » 04 июн 2017, 18:17

Дальше:

Код: Выделить всё

fun <T> asList(vararg ts: T): List<T> {            // переменное число параметров
    val result = ArrayList<T>()
    for (t in ts)                                  // ts - это массив (Array)
        result.add(t)
    return result
}

fun test2(): Unit {
    fun printListInt(list: List<Int>) {
        for (t in list)
            print("<" + t.toString() + "> ")
        println()
    }
    val list = asList(1, 2, 3)
    val a = arrayOf(1, 2, 3)
    printListInt( list )
    printListInt( asList(-1, 0, *a, 4) )           // оператор spread: *a
}
Здесь показано:
- как функция asList может иметь переменное (произвольное) число параметров вызова, причём переменные параметры могут иметь различающиеся типы, в том числе и лямбда-функции (в {} записи), аргумент отмеченный квалификатором vararg может быть только один, и желательно, чтобы он был последним в списке аргументов (если не последний, то последующие можно достать, но хлопотно: ключевым вызовом, см. далее);
- как могут определяться локальные (вложенные) определения функций (printListInt), причём глубина вложенности может быть произвольной;
- как контейнер (Array<Int> a) "разворачивается" в последовательность его элементов операцией spread: знак * перед именем a;

Код: Выделить всё

[olej@dell tests]$ java -jar test_fun.jar 3
3 ------------------------------------------
<1> <2> <3>
<-1> <0> <1> <2> <3> <4>

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: код Kotlin & Java

Непрочитанное сообщение Olej » 04 июн 2017, 19:41

Olej писал(а):Дальше:
Лямбда-выражения и анонимные функции:

Код: Выделить всё

val median: (Double, Double) -> Double = { x, y -> x + y / 2.0 }

fun test3(): Unit {
    val a = 5.0; val b = 12.0;
    var c = median(a, b);
    println( c )
    val ar = Array(7, { i -> (i * i - 10).toString() })
    for (t in ar)
        print( t + " " )
    println()
}
Здесь константа median получает функциональное значение, записанное лямбда-выражением, а в инициализации ar 2-м параметром используется анонимная функция (фабрика) для инициализации элементов.

Код: Выделить всё

[olej@dell tests]$ java -jar test_fun.jar 4
4 ------------------------------------------
11.0
-10 -9 -6 -1 6 15 26 

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: код Kotlin & Java

Непрочитанное сообщение Olej » 04 июн 2017, 20:39

Olej писал(а):Дальше:
Хвостовая рекурсия:

Код: Выделить всё

tailrec fun findFixPoint(x: Double = 1.0): Double
        = if (x == cos(x)) x else findFixPoint(cos(x))
Ничего особенного в коде, такое можно написать на любом языке программирования ... только здесь если а). стоит квалификатор tailrec и б). это действительно хвостовая рекурсия (т.е. рекурсивный вызов - последний в последовательности операторов), то компилятор вместо рекурсивных вызовов превратит это примерно в следующее:

Код: Выделить всё

fun findFixPoint(): Double {
    var x = 1.0
    while (true) {
        val y = cos(x)
        if (x == y) return y
        x = y
    }
}
Т.е. последовательность рекурсивных вызовов автоматически превратится в цикл.
Хвостовая рекурсия
Хвостовая рекурсия — частный случай рекурсии, при котором любой рекурсивный вызов является последней операцией перед возвратом из функции.[1] Подобный вид рекурсии примечателен тем, что может быть легко заменён на итерацию путём формальной и гарантированно корректной перестройки кода функции. Оптимизация хвостовой рекурсии путём преобразования её в плоскую итерацию реализована во многих оптимизирующих компиляторах. В некоторых функциональных языках программирования спецификация гарантирует обязательную оптимизацию хвостовой рекурсии.
Иногда это даст радикальное повышение производительности!
Выполнение:

Код: Выделить всё

[olej@dell tests]$ java -jar test_fun.jar 5
5 ------------------------------------------
0.7390851332151607

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: код Kotlin & Java

Непрочитанное сообщение Olej » 04 июн 2017, 22:35

Olej писал(а): если а). стоит квалификатор tailrec и б). это действительно хвостовая рекурсия (т.е. рекурсивный вызов - последний в последовательности операторов), то компилятор вместо рекурсивных вызовов превратит это примерно в следующее:
Вопрос относительно хвостовой рекурсии настолько интересный, что на нём стоит остановиться на время и уточниться...
Начиная с того, что традиционное вычисление факториала, которое показывают школьникам, вот на C, например:

Код: Выделить всё

int factorial (int n) {
    return (n==0) ? 1 : n*factorial(n-1);
}
Оно не является хвостовой рекурсией, т.к., хотя вызов factorial(n-1) и записан последним, но выполняется последним оператор умножения на n.
Написал 2 маленьких тестовых программки на Kotlin:
- factr.kt - здесь нет хвостовой рекурсии:

Код: Выделить всё

fun factorial(n: Long): Double {
    return if (n == 0.toLong()) 1.toDouble() else n*factorial(n-1)
}

fun main( args: Array<String> ) {
   val x = args[0].toLong()
   println( factorial( x ) )
}
- factt.kt - здесь разворачивается хвостовая рекурсия:

Код: Выделить всё

tailrec fun fac_times(n: Long, acc: Long): Double {
    return if (n == 0.toLong()) acc.toDouble() else fac_times(n - 1, acc * n)
}

fun factorial(n: Long): Double {
    return fac_times(n, 1)
}

fun main( args: Array<String> ) {
   val x = args[0].toLong()
   println( factorial( x ) )
}
Выполняем:

Код: Выделить всё

[olej@dell tests]$ kotlinc factr.kt -include-runtime -d factr.jar

[olej@dell tests]$ kotlinc factt.kt -include-runtime -d factt.jar

[olej@dell tests]$ java -jar factr.jar 20
2.43290200817664E18

[olej@dell tests]$ java -jar factt.jar 20
2.43290200817664E18
А вот если я припишу tailrec в 1-ю реализацию:

Код: Выделить всё

tailrec fun factorial(n: Long): Double {
    return if (n == 0.toLong()) 1.toDouble() else n*factorial(n-1)
}
...
То компилятор очень лихо распознаёт эту ситуацию!:

Код: Выделить всё

[olej@dell tests]$ kotlinc factr.kt -include-runtime -d factr.jar
factr.kt:1:1: warning: a function is marked as tail-recursive but no tail calls are found
tailrec fun factorial(n: Long): Double {
^
factr.kt:2:53: warning: recursive call is not a tail call
    return if (n == 0.toLong()) 1.toDouble() else n*factorial(n-1)
                                                    ^
Вложения
factr.kt
(195 байт) 86 скачиваний
factt.kt
(288 байт) 92 скачивания

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: код Kotlin & Java

Непрочитанное сообщение Olej » 05 июн 2017, 14:05

Olej писал(а): Тестирую функции (и функции-методы), как наиболее сильно отличающуюся часть...
Не менее интересной (и отличительной) стороной Kotlin есть их концепция классов и их иерархии (см. Классы и наследование).
Их классы позволяют наследование и переопределение методов только (и там) если автор базового класса (суперкласса) явно разрешает это в определении класса, указывая oprn и override (или определив его как abstract):
Ключевое слово open является противоположностью слову final в Java: оно позволяет другим классам наследоваться от данного. По умолчанию, все классы в Kotlin имеют статус final,
Стойте! Как мне теперь хакнуть свои библиотеки?

При нашем подходе к переопределению классов и их членов (которые по дефолту final) будет сложно унаследоваться от чего-нибудь внутри используемых вами библиотек для того, чтобы переопределить не предназначенный для этого метод и внедрить туда свой гнусный хак.
Там есть ещё ряд интересных частностей:
Изолированные классы (sealed classes)
Классы данных (data classes)
Вложенные классы (nested classes)
Классы-перечисления (enum classes)
Так что для тестирования разных замысловатых конструкций с классами имеет смысл строить отдельное тестирующее приложение, по типу такого же как для функций: test_class.kt.

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: код Kotlin & Java

Непрочитанное сообщение Olej » 26 окт 2017, 14:48

Вышла 1-я книга, перевод, на русском:
Изображение

P.S. Кстати, это ... обратный перевод - книга написана русскоязычными авторами на английском языке.

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: код Kotlin & Java

Непрочитанное сообщение Olej » 16 апр 2018, 09:59

Swift is like Kotlin
Прямое сравнение конструкций:
BASICS
...
COLLECTIONS
...
FUNCTIONS
...
CLASSES
...

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 2 гостя