Learn You Scala for Great Good! Часть 3
Сегодня я продолжу разговор, начатый здесь (пример про Гарды и калькулятор лишнего веса), и продолженный здесь (вычисление максимального числа из списка) и здесь (пример про максимум для произвольного списка). Все это касалось изучения функционального программирования по книге Learn You a Haskell for Great Good!
Функция replicate
Следующая функция из главы, посвященной рекурсии, называется replicate. Она получает в качестве первого аргумента число повторений, а в качестве второго — элемент, который нужно повторить нужное число раз. И возвращает список, состоящий из повтореного нужное число раз элемента.
Итак, Haskell:
replicate :: (Num i, Ord i) => i -> a -> [a]
replicate n x
| n <= 0 = []
| otherwise = x:replicate (n-1) x
Как и в предыдущем примере, мы видим typeclass Ord
, которого нет в Scala (но не в сторонних библиотеках).
Сперва напишем пример для целых чисел:
def replicate(i:Int, x: Int):List[Int] = {
i match {
case n if n <= 0 => Nil
case n => x::replicate(n-1, x)
}
}
println(replicate(7, 9))
Но мы можем легко привести эту функцию к общему виду, заменив Int
на Any
:
def replicate(i:, x: Any):List[Any] = {
i match {
case n if n <= 0 => Nil
case n => x::replicate(n-1, x)
}
}
println(replicate(12, 'a'))
Хотя правильнее будет записать это так:
def replicate[A](i:, x: A):List[A] = {
i match {
case n if n <= 0 => Nil
case n => x::replicate(n-1, x)
}
}
println(replicate(12, 'a'))
Теперь функция будет работать с определенным типом, а не с Any
.
Функция take
Эта функция принимает число n
и список l
и возвращает другой список, содержащий первые n
элементов списка l
.
Haskell:
take :: (Num i, Ord i) => i -> [a] -> [a]
take n _
| n <= 0 = []
take _ [] = []
take n (x:xs) = x : take (n-1) xs
В Scala это выглядит так:
def take[A](n:Int, l:List[A]): List[A] = {
(n, l) match {
case (n, _) if n <= 0 => Nil
case (_, Nil) => Nil
case (n, x::xs) => x::take(n-1, xs)
}
}
println(take(3, List(5, 34, 99, 8, 567, 222)))
println(take(5, List("5", "34", "99", "8", "567", "222")))
Используется сравнение с образом (pattern matching):
- Если n равно или меньше нуля, мы получаем пустой лист.
- Если второй аргумент — пустой лист, то возвращается пустой лист.
- Если же второй аргумент список
x::xs
, то функция вызывается сноваx::take(n-1, xs)
Функция reverse
Данная функция просто «переворачивает» список:
Haskell:
reverse :: [a] -> [a]
reverse [] = []
reverse (x:xs) = reverse xs ++ [x]
Scala:
def reverse[A](l: List[A]):List[A] = {
l match {
case Nil => Nil
case x::xs => (reverse(xs) ::: x::Nil)
}
}
println(reverse(1::2::3::4::4::5::6::7::Nil))
Все очень просто: функция берет первый элемент списка и бросает его в конец, затем вызывается рекурсивно к оставшейся части.