2016-09-01 16 views
6

Одна вещь, которую я пропускал в Perl 6, является intersperse функция like Haskell has:Как написать `intersperse` функцию в Perl 6

Функция пересыпать принимает элемент и список и` вкрапляет этот элемент между элементами списка.

E.g. это:

intersperse <X Y>, (<a b>, <c d>, <e f>); 

... должен вернуть эту последовательность:

<a b>, <X Y>, <c d>, <X Y>, <e f> 

Так что я пытался реализовать себя в качестве пользовательской функции. Для максимального повторного использования, он должен:

  1. Поддержка любого типа объекта (в том числе и список Nil) в качестве элементов.
  2. Не меняйте контейнеризацию элементов в любом случае.
  3. Не сглаживать или иным образом влиять на внутреннюю структуру элементов.
  4. Верните ленивую последовательность, если входной список задан как ленивая последовательность, так что его можно использовать в бесконечных последовательностях, как в intersperse 42, 1..Inf.

То, что я придумал до сих пор, это:

sub intersperse (\element, +list) { 
    ((element xx *) Z list).map(|*)[1..*] 
} 

То есть: Бесконечно повторять элемент перемежаться, пронестись его со списком, а затем использовать map для slip каждого кортежа чтобы удалить слой вложенности, добавленный zip, без выравнивания исходных элементов, а затем использовать индекс массива, чтобы удалить ведущее повторение вкрапленного элемента.

Он удовлетворяет требования 1-3, но не 4, так как индекс массива работает охотно (т.е. полностью перебирает последовательность ввода, а затем возвращает не ленивый List) и, таким образом, вызывает эту функцию, чтобы повиснуть при дан бесконечная последовательность.

Что было бы хорошим способом реализовать эту функцию, чтобы она удовлетворяла всем 4 требованиям?

+2

Inter esting. Вид вроде 'List' версии' join'. –

ответ

9

Я не особенно доволен решениями я придумываю, но здесь они идут:

sub intersperse (\element, +list) { 
    map { ((element xx *) Z list).map(|*)[$_] }, 
     1..(list.is-lazy ?? Inf !! list.elems * 2 - 1); 
} 

sub intersperse (\element, +list) { 
    gather for list { 
     FIRST .take, next; 
     take slip element, $_; 
    } 
} 

sub intersperse (\element, +list) { 
    list.map({ slip element, $_ }) does role { 
     method iterator { 
      my \it = callsame; 
      it.pull-one; 
      it; 
     } 
    } 
} 

Возможно, это будет служить как вдохновение для кого-то другого, чтобы придумать что-то лучшее ...

+0

Ой, что первый превращает его из 'O (n)' в 'O (n²)', не так ли? – smls

+0

«Собирать /' взять », кажется, хороший подход, хотя я и не думал об этом. Я не думаю, что оператор-запятая гарантированно будет точкой последовательности, как в C, хотя, вероятно, я бы написал такую ​​строку как 'FIRST {take $ _; next} '. – smls

+0

ну, я бы не рекомендовал первый подход: это было именно то, что я придумал, чтобы твоя оригинальная идея работала; Я подозреваю, что может быть лучший способ сделать это, но это было не сразу очевидно для меня; относительно оператора запятой, он должен быть безопасным, поскольку он оценивается слева направо; Я не думаю, что это где-то на самом деле документировано - если это не кто-то должен сделать это и добавить соответствующий спектакль – Christoph