【问题标题】:How to write an `intersperse` function in Perl 6如何在 Perl 6 中编写“intersperse”函数
【发布时间】:2017-01-09 00:08:18
【问题描述】:

我在 Perl 6 中缺少的一件事是 intersperse 函数 like Haskell has

intersperse 函数接受一个元素和一个列表,并将该元素“穿插”在列表的元素之间。

例如这个:

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

...应该返回这个序列:

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

所以我一直在尝试自己将其实现为自定义函数。 为了实现最大的可重用性,它应该:

  1. 支持任何类型的对象(包括 List 和 Nil)作为元素。
  2. 不得以任何方式更改元素的容器化。
  3. 不会以任何方式展平或以其他方式影响元素的内部结构。
  4. 如果输入列表作为惰性序列给出,则返回惰性序列,以便它可以用于无限序列,如intersperse 42, 1..Inf

到目前为止,我想出的是这样的:

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

即:将要穿插的元素无限重复,用list压缩,然后用mapslip每个元组,去掉zip加的嵌套层,不压扁原来的元素,然后使用数组下标删除穿插元素的前导重复。

满足1-3的要求,但不满足4,因为数组下标急切地操作(即完全迭代输入序列,然后返回一个非惰性列表)从而导致这个函数给定无限序列时挂起。

实现此功能以满足所有 4 个要求的好方法是什么?

【问题讨论】:

  • 有趣。有点像List 版本的join

标签: list raku lazy-sequences


【解决方案1】:

我对我提出的解决方案并不特别满意,但它们就在这里:

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;
        }
    }
}

也许它会激发其他人想出更好的东西......

【讨论】:

  • 哎呀,第一个把它从O(n) 变成O(n²),不是吗?
  • gather/take 似乎是一个不错的方法,但我没有想到这一点。我不认为逗号操作符保证是一个序列点,就像它在 C 中一样,所以我可能会写像FIRST { take $_; next } 这样的行。
  • 好吧,我不推荐第一种方法:这正是我想出的让你最初的想法奏效的方法;我怀疑可能有更好的方法来做到这一点,但这对我来说并不是很明显;关于逗号运算符,它应该是安全的,因为它是从左到右进行评估的;我认为这实际上没有记录在任何地方 - 如果不是有人应该这样做并添加相应的 spectest
  • S03 design.perl6.org/S03.html#Sequence_points 明确表示依赖未指定为序列点的运算符的顺序副作用是错误的。它没有说明逗号属于哪个类别,但我隐约记得 Larry 或其他人在 IRC(几个月前)上说过,现在不能保证它是一个序列点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-08
  • 2017-08-22
  • 2018-01-28
  • 1970-01-01
  • 1970-01-01
  • 2017-08-12
相关资源
最近更新 更多