?

Log in

No account? Create an account
Язык программирования Scala

> Recent Entries
> Archive
> Friends
> Profile
> Официальный сайт Scala
> previous 10 entries

December 25th, 2017


potan
07:33 pm - Нестандартные запросы в Slick.
Можно ли в Slick сформировать запрос, аналогичный такому?
select * from jr
       where (id1, id2) in (
         ('000002e4-6b99-45ac-9187-4d00b7b457fc', 'a1c4fa6f-370c-11e6-bb37-f832e4bd9ad8'),
         ('00011eaf-f65d-42a7-ae54-3b16dff65f22','4834f6b6-af4a-4cc5-88d6-924a84f0ef31'));

Стардарт SQL такое не поддерживает, но и MySQL, и Postgres его выполняют. Но в Slick как сделать из двух slick.lifted.Rep[String] одну slick.lifted.Rep[(String,String)] я не нашел. Это вообще возможно?

(8 comments | Leave a comment)

April 1st, 2017


mikkim08
06:52 pm - Event Sourcing и Моноид
Вот говорят, что есть т.н. event sourcing, когда в базу записываются только event-ы, например
"изменить значения таких-то полей у такой-то instance-a такой-то entity", "удалить такой-то instance" и т.д. А потом по последовательности этих event-ов можно восстановить весь state.

Выглядит это восстановление так: каждый event превращается в функцию: State => State, которые потом "композируются". Поскольку A => A с операцией andThen образуют моноид, то процесс восстановения можно записать:
def createTransform(e: Event): State => State = ...
val transform = events foldMap createTransform
transform(state)
Логично ?
Даёт ли нам что-то использование абстракции моноида ?
Есть ли какие-нибудь примера использования моноида для event sourcing ?

(11 comments | Leave a comment)

March 18th, 2017


mikkim08
10:37 pm - Общее окончание
Услышал, что в одной конторе для первичного отбора кандидатов просили написать программу поиска самого длинного общего окончания у нескольких строк. Задача несложная, но хочется написать функционально, просто и эффективно, т.е. не менее эффективно чем императивное решение.

Для 2х строк получилось так:
def commonSuffix(s1: String, s2: String): String = {
  val n = (s1.reverseIterator zip s2.reverseIterator)
            .takeWhile {case (a, b) => a == b}
            .size
  s1.substring(s1.length - n) // is substring efficient ?
}
А как найти общее окончание для нескольких строк ?

(2 comments | Leave a comment)

March 11th, 2017


mikkim08
10:03 pm
Предположим у нас есть несколько функций, которые вызываются при наступлении некоторого события. Так например при добавлении нового поста в ЖЖ нужно послать почту всем подписчикам, а также опубликовать ссылку на пост в социальных сетях, твитере и так далее.
case class NewPost(...)
type NewPostSubscriber = NewPost => Either[Exception, Unit]
Допустим, что все эти функции не зависят друг от друга и либо не возвращают ничего в случае удачного завершения, либо возвращают ошибку. Предположим также, что все возвращаемые ошибки нужно аккумулировать.

Теперь можно воспользоваться тем, что функции типа NewPostSubscriber можно превратить в функции NewPost => ValidatedNel[Exception, Unit], которые образуют моноид. Тогда вызов тех функций будет выглядеть так:
// invoke all subscribers with a NewPost event

def fireNewPost(event: NewPost, subscribers: List[NewPostSubscriber]) = {
  val invokeAll = subscribers foldMap { s => s map (_.toValidatedNel) } map (_.toEither)
  invokeAll(event)
}
Как вам такое решение ?

(5 comments | Leave a comment)

March 7th, 2017


mikkim08
09:51 am - Моноид для ключей командной строки
Наткнулся на моноид для ключей командной строки. Кто-нибудь имплементировал такое решение на Скале ?

(1 comment | Leave a comment)

March 6th, 2017


mikkim08
06:59 pm - Задачка из Твитера
Я прочитал про решение на Скале одной известной задачки.
Оно мне показалось слишком сложным. А как бы вы решали эту задачку ?

(Leave a comment)

mikkim08
06:21 pm - Функциональное Решение
Я тут попробовал продемонстрировать на простом и знакомом примере преимущество функционального подхода.
Надеюсь, что в решении ничего не упустил. Насколько корректным и, главное, убедительным Вам представляется это решение ?

--

Допустим, нам нужно найти одну какую-нибудь пару элементов в отсортированном массиве целых чисел, сумма которых равна заданному числу target.

Решение заключается в проходе по массиву слева направо и справа налево с помощью двух индексов right и left: right указывает на правый текущий элемент, а left на левый. На каждой итерации проверяется сумма текущего левого и правого элементов. Если она равна target'у, то искомая пара найдена, если меньше, то right сдвигается влево, а если больше, то left сдвигается вправо.
def pair(a: Array[Int], target: Int): Option[(Int, Int)] = {

    var left = 0
    var right = a.length - 1
    var result: Option[(Int, Int)] = None
    while (left < right && result.isEmpty) {
      (a(left), a(right)) match {
        case (x, y) if x + y == target => result = Some(x, y)
        case (x, y) if x + y < target => left = left + 1
        case (x, y) if x + y > target => right = right - 1
      }
    }
    result
  }
А что если нам нужно найти не одну а все пары элементов массива, сумма которых равна target'у ?
Тогда придётся переписать наше решение:
def pairs(a: Array[Int], target: Int): List[(Int, Int)] = {

    var left = 0
    var right = a.length - 1
    var result: List[(Int, Int)] = List()
    while (left < right) {
      (a(left), a(right)) match {
        case (x, y) if x + y == target => result = result :+ (x, y); left = left + 1
        case (x, y) if x + y < target => left = left + 1
        case (x, y) if x + y > target => right = right - 1
      }
    }
    result
  }
А можно обойтись без copy-paste ?

Оказывается можно, если написать "функциональное" решение !
Сначала мы сформируем ленивый список (stream) всех "кандидатов":
def streamOfPairs(a: Array[Int], target: Int): Stream[(Int, Int)] =
    Stream.iterate(a) { xs => if (xs.head + xs.last > target) xs.init else xs.tail }
      .take(a.length - 1)
      .map { xs => (xs.head, xs.last) }
А теперь из полученного "стрима" легко получим как одну, так и все искомые пары:
 def pair(a: Array[Int], target: Int): Option[(Int, Int)] =
    streamOfPairs(a, target) find { case (x, y) => x + y == target }

  def pairs(a: Array[Int], target: Int): List[(Int, Int)] =
    (streamOfPairs(a, target) filter { case (x, y) => x + y == target }).toList
Как видим, "функциональный подход" помог нам избавиться от copy-paste и упростить решение.

(6 comments | Leave a comment)

January 17th, 2017


mikkim08
06:46 pm - Как реализовать fallback ?
Возвращаясь к ранее приведённому примеру: Допустим у нас есть функция для чтения файла
type Content = String
def readFile(fileName: String): Try[Content] = ???
Нам нужно написать функцию readFile(fileNames: List[String]), которая бы читала все файлы по очереди и возвращала содержимое первого успешно считанного файла. Тогда эта функция будет выглядеть так:
def readFile(fileNames: List[String]): Either[Content, List[Throwable]]
А что, если нам нужно вместе с содержимым получить список ошибок, которые случились до того ? Тогда функция будет:
def readFile(fileNames: List[String]): Either[(Content, List[Throwable]), List[Throwable]]
Как написать такую функцию ?

(29 comments | Leave a comment)

January 15th, 2017


mikkim08
05:18 pm - Как решить предыдущий пример без моноида ?
Допустим, нужно прочитать обязательно все файлы и вернуть либо содержимое всех этих файлов, либо список ошибок чтения.
def readFiles(fileNames: List[String]): Either[List[Throwable], List[String]] = ???
Предположим у нас есть уже функция для чтения одного файла:
def readFile(fileName: String): Try[String] = ???
Как написать функцию readFiles без использования моноидов как в предыдущем посте ?

Было предложено сначала построить val tries: List[Try[String]] = fileNames map readFile, а дальше из этого списка получить Either[List[Throwable], List[String]].
Будет ли это проще чем решение в предыдущем посте ?

(31 comments | Leave a comment)

January 14th, 2017


mikkim08
08:56 pm - Пример использования моноида. Накопление ошибок
Допустим, нам нужно прочитать несколько файлов. Функция readFiles принимает список имён файлов, которые нужно прочитать, и возвращает либо список содержимого файлов (пусть это будут строки для простоты), либо список ошибок, из-за которых нам не удалось эти файлы прочитать.
def readFiles(fileNames: List[String]): Either[NonEmptyList[Throwable], List[String]] = ???
Предположим у нас уже есть функция, которая читает один файл:
def readFile(fileName: String): Try[String] = ???
Наша задача написать функцию readFiles.
def readFiles(fileNames: List[String]): Either[NonEmptyList[Throwable], List[String]] = {
  val read = {fn: String => readFile(fn).toEither.map(_ :: Nil).toValidatedNel}
  val validated = fileNames foldMap read
  validated.toEither
}
Что тут можно улучшить или упростить ?
Я хочу использовать этот пример для демонстрации полезности моноида: List это моноид. Validated[A, B], где А и B моноиды -- тоже моноид. А для работы с моноидами у нас есть удобная функция foldMap.

Ещё примеры использования моноида:

   1) написать readFiles2 (partial results), чтобы та возвращала и список содержимого, и список ошибок чтения
   2) написать readFiles3 (fallback), чтобы та возвращала содержимое первого файла, который удалось прочитать, и список ошибок, случившихся до того.

Можно ли для (2) использовать моноид ?

(14 comments | Leave a comment)

> previous 10 entries
> Go to Top
LiveJournal.com