Обновить
21
Иван Ларин@ivanlardis

Пользователь

14
Подписчики
Отправить сообщение
расскажите как изменится решение, чтобы отсортировать пользователей не как на скриншоте, а в натуральном порядке (user8, user9, user10, user11)

Как мы обсуждали ранее

Списки могут работать с Function2, которые являются моноидом
Iterable.sort(function2: Function2Monoid<A, Order>)

Function2 имеет тип (A, A) -> Order. Как раз она решает что и как сортировать
То есть если нам нужен любой другой вид сравнения,
мы реализуем Function2 по-другому

val <A> ((A) -> String).customComparator
    get():Function2Monoid<A, Order> = Function2Monoid<A, Order> { a, other ->
        val aString = invoke(a)
        val otherString = invoke(other)

        //сравниваем значения строк как нам нужно
        //возвращаем Order, который нам нужен при сравнении
        return@Function2Monoid Order.EQ
    }

И после этого можем его использовать в сортировке

 users.sort(
        User::name.stringComparator +
        User::address.andThen(Address::city).comparator
    ).forEach(::println)


Как добавить сортировку по адресу, если у отдельных пользователей адрес может быть не указан?

Учим наши функции работать с nullable)

infix fun <A, B, C> ((A) -> B?).andThen(g: (B) -> C): (A) -> C? = { a: A ->
    this(a)?.let(g)
}

val <A, B : Comparable<B>> ((A) -> B?).comparator
    get():Function2Monoid<A, Order> = Function2Monoid<A, Order> { a, other ->
        val b = invoke(a)
        val bOther = invoke(other)
        when {
            b != null && bOther != null -> b.compare(bOther)
            b != null -> Order.GT
            bOther != null -> Order.LT
            else -> Order.EQ
        }
    }

Плюс для улучшения Api можно посмотреть в сторону оптики

val <A, B : Comparable<B>> Lens<A, B>.comparator
    get():Function2Monoid<A, Order> = ::get.comparator

val <A, B : Comparable<B>> Optional<A, B>.comparator
    get():Function2Monoid<A, Order> = (::getOption andThen Option<B>::orNull).comparator


    users.sort(
        AppUser.name.comparator +
        AppUser.address.city.comparator
    ).forEach(::println)

зачем Order реализует интерфейс Monoid?

Это сделано для очевидности доказательства

Чтобы доказать, что (A, A) -> Order.
Мы должны были доказать, что Order это моноид

ComparatorMonoid::plus — не делает ли данная функция лишние сравнения?

Правильное замечание, делает.
Но тут есть два пути развития
1)Следующим шагом, после того, как мы доказали что (A, A) -> Order моноид
Оптимизировать его, чтобы он не делал лишних сравнений
fun interface ComparatorMonoid<A> : Monoid<ComparatorMonoid<A>> {
    fun compare(a: A, other: A): Order

    override fun plus(rh: ComparatorMonoid<A>) =
        ComparatorMonoid<A> { a, other ->
            val order = compare(a, other)
            when (order) {
                Order.EQ -> rh.compare(a, other)
                else -> order
            }
        }

    override fun empty() = ComparatorMonoid<A> { _, _ -> Order.EQ }
}


2)Либо пойти по правильному функциональному пути
Так, как обсуждалось ранее
Если область значения функции моноид, то такая функция также моноид
Реализуем это в коде

fun interface Function2Monoid<A, B : Monoid<B>> : Monoid<Function2Monoid<A, B>> {

    fun invoke(a: A, other: A): B

    override fun plus(rh: Function2Monoid<A, B>): Function2Monoid<A, B> =
        Function2Monoid { a, other -> invoke(a, other) + rh.invoke(a, other) }

    override fun empty() = Function2Monoid<A, B> { a, a1 ->
        invoke(a, a1).empty()
    }
}


И теперь нам не нужно создавать ComparatorMonoid
Мы можем пользоваться Function2Monoid
Тут и пригодится знание о том, что Order это моноид, которое легко доказать
Переписав функции в такой вид

fun <A> Iterable<A>.sort(comparatorMonoid: Function2Monoid<A, Order>) =
    sortedWith { a, b -> comparatorMonoid.invoke(a, b).compareValue }

fun <A : Comparable<A>> A.compare(other: A) = when {
    this > other -> Order.GT
    this == other -> Order.EQ
    else -> Order.LT
}

val <A, B : Comparable<B>> ((A) -> B).comparator
    get():Function2Monoid<A, Order> = Function2Monoid<A, Order> { a, b ->
        invoke(a).compare(invoke(b))
    }


Не хотел этим усложнять статью.
Но, видимо, стоило.
Хотелось поменьше теории) Побольше практики
Соглашусь с вами, наверное, некорректно сравнивать продукт на ранней стадии разработки с готовым продуктом. Но моя цель была сравнить, на каком этапе разработки сейчас представленные на конференциях продукты.
Как мне показалась, это интересная тема.

Насчет того, что сначала стоило посмотреть первоисточник — я с первоисточника и начал. И я понимал, что Compose в очень ранней стадии разработки и понимал, что гугл сказал, что хочет собрать обратную связь от разработчиков. Но при этом всем, когда начал смотреть Compose, в голове держал, то что Google уже создал Flutter, который вызвал большой интерес у Android разработчиков. И что у Google уже есть обратная связь по Flutter от тех же разработчиков, от которых просит обратную связь по Compose. И ждал от Compose, то что он будет реализован на основе отзывов о Flutter, хоть и на уровне концепции. А по факту, сейчас Compose — это пара полурабочих вьюх. Какого отзыва Google ждет и может ждать на основе такого? Складывается ощущение, что Google было важно анонсировать хоть что-то.

Насчет статьи надеюсь, хоть кому-то она была полезна.
Во Flutter при той же реализации что и в React — Native c помощью Rx не стопиться
Вот какая реализация во флатере
  subscription = new Observable<ModelTimer>.fromIterable(list)
        .interval(new Duration(seconds: 1))
        .listen((modelTimer) =>

Я объективно ответил на комментарий — проверив, запустив разработанные приложения
Я реализовывал таймер через RxJs interval
 this.subscription = interval(1000)
            .pipe(take(list.length))
            .subscribe(a => {
                this.timer.show(list[a])
            })

такой код в моем презентере. Именно в такой реализации таймер стопится. После вопроса я специально запустил приложение и проверил.
Приведенную вами библиотеку я не рассматривал. Но видно, что либа строит мост в native и там уже реализуется таймер. Понятное дело, мне ничто не мешает сделать foreground service в Android, запустить в нем таймер и общатьсяс c js через мост, даже без использование либы. И такой таймер на родных прошивках в Android убиваться не будет в принципе.
То что я писал это про выполнение JS
Так как приложения очень маленькие — скорость их работы визуально одинакова. Сначала я думал сравнить сколько будут весить Apk для Android. Но сравнивать размеры тоже было некорректно, т.к.:
1)В React Native был добавлен Realm, нативная часть которого очень утяжелила Apk.
2)В Android я запихнул кучу библиотек, без которых я считаю, как “истинный Android разработчик“, приложению не обойтись.
1)Почему не попал ionic? Потому что, лично для меня, были интересны React Native и Flutter
2)Главное для меня при разработке приложений — это их поддерживаемость. Да, я понимаю, что кроссплатформа быстрее и кажется на первый взгляд выгоднее для бизнеса. Но в долгосрочной перспективе, по моему мнению, она себя не окупит, так как все равно придется писать мост к нативу, когда нужно будет писать что-то специфическое под платформу. Хотя, если я буду писать домашний проект, я склонюсь к Flutter.
Тут нужно обратить внимание: при уходе в фон убивается процесс приложения или нет. В ios процесс убивается у свернутого приложения, через некоторое время. И убийство процесса никакой из вариантов не переживет. А если рассматривать ситуацию сворачивания в Android до момента убийства процесса, то Flutter нормально работает и время не останавливается. А в React Native таймер зависает
Жор батарейки мною не рассматривался. Насчет скорости — разрабатываемые приложения очень маленькие. Всего 3 экрана. И разницу в скорости визуально ощутить не получилось, так как все приложения выглядят шустро. А программное измерение скорости я не производил
Выдержка из статьи про RxSwift
оператор — observeOn
Указывает, на каком Scheduler должен выполнять свою работу Observer, особенно критично при работе с GUI.
example("\"observeOn\"") {
    DispatchQueue.global(qos: .background).async {
        Observable
            .of(1, 2, 3)
            .observeOn(MainScheduler.instance)
            .subscribe({ event in
                print("Main: \(Thread.isMainThread)\t\tEvent: \(event)")
            })
        Observable
            .of("A", "B", "C")
            .subscribe({ event in
                print("Main: \(Thread.isMainThread)\t\tEvent: \(event)")
            })
    }
}

Консоль:
Main: false Event: next(A)
Main: false Event: next(B)
Main: true Event: next(1)
Main: true Event: next(2)
Main: true Event: next(3)
Main: true Event: completed
Main: false Event: next(C)
Main: false Event: completed
Спасибо поправил. Там речь о времени
1) Искать библиотеки можно тут
2) Не могу компетентно ответить на этот вопрос. Но есть русскоязычный чат о Dart в telegram, думаю там вам с удовольствием ответят.
Я пытался сослаться на роман. В одном из переводов все-таки Мечтают, а не Снятся

Информация

В рейтинге
Не участвует
Дата рождения
Зарегистрирован
Активность