Картирование подсписки в Scala
Я знаю, что функция map берет каждый элемент списка (последовательность) и применяет к нему функцию. Рекурсивно (и без соблюдения условий завершения и т. д.)
map(s, f) = f(s.head) :: map(s.tail, f)
Я ищу функцию, которая делает что-то вроде
foo(s, f) = f(s) :: map(s.tail, f).
Так 'картограф', где отображение функция вызывается на подсписки, а не отдельные элементы. В терминах lisp я ищу maplist, а не mapcar. Существует ли что-то подобное, или я должен свернуть мой собственный (или использовать рекурсию)?
альтернативно, я бы взял функцию, которая принимает в качестве входного последовательность и возвращает последовательность подпоследовательностей от середины до конца, т. е.
bar(s, f) = s :: bar(s.tail, f)
3 ответов
/ * этот подход определяет mapList в терминах другого полезного метода, называемого tails. Как и Даниэль, я помещу его в неявное расширение для списка, но это чисто вопрос вкуса */
implicit def richerList[A](list : List[A]) = new {
/* вот метод под названием tails, который возвращает каждый возможный хвост в списке. Это хвост рекурсивный, поэтому он не будет взрываться в больших списках. Обратите внимание, что он немного отличается от функции Haskell с тем же именем. Версия Haskell всегда добавляет пустой список в результат */
def tails : List[List[A]] = {
def loop(ls : List[A], accum : List[List[A]]) : List[List[A]] = ls match {
case _ :: tail => loop(tail, ls :: accum)
case _ => accum
}
loop(list, Nil).reverse
}
/* вот как выглядит использование хвостов
scala> "abc".toList.tails
res0: List[List[Char]] = List(List(a, b, c), List(b, c), List(c))
*/
/* теперь мы можем определить mapList на основе решка */
def mapList[B](f : List[A] => B) = tails map f
}
/* и вот как выглядит использование mapList
scala> "abc".toList mapList (_.reverse.mkString)
res1: List[String] = List(cba, cb, c)
*/
вы в основном определили, что ищете в псевдокоде - поэтому легко добавить такой метод в список Scala, используя неявные преобразования:
object ExtendedList{
implicit def List2ExtendedList[A](l:List[A])=new ExtendedList(l)
}
class ExtendedList[A](l:List[A]){
import ExtendedList._
def mapList[B](f:List[A]=>B):List[B]=l.length match {
case 0 => List()
case _ => f(l)::l.tail.mapList(f)
}
}
object Test extends Application{
import ExtendedList._
val test = List(5,4,3,2,1)
assert(List(15,10,6,3,1)==test.mapList{l=>(0/:l){_+_}})
}
это то, что вы ищете?
другой ответ близок, но вы должны никогда использовать List#length
если это не абсолютно необходимо. В частности, это делает его решение O (n^2) когда проблема inherantly O (n). Вот очищенная версия:
implicit def addListSyntax[A](list: List[A]) = new {
def mapList[B](f: List[A]=>B) = {
// use inner function to avoid repeated conversions
def loop(list: List[A]): List[B] = list match {
case ls @ (_ :: tail) => f(ls) :: loop(tail)
case Nil => Nil
}
loop(list)
}
}
и, отвечая на ваш изначальный вопрос: нет, нет никакого способа сделать это с помощью стандартных методов. Мне на самом деле немного любопытно, почему вы хотите что-то подобное...