简体   繁体   English

在 Raku 中,如何编写等效于 Haskell 的 span 函数?

[英]In Raku, how does one write the equivalent of Haskell's span function?


In Raku, how does one write the equivalent of Haskell's span function?在 Raku 中,如何编写等效于 Haskell 的span函数?

In Haskell, given a predicate and a list, one can split the list into two parts:在 Haskell 中,给定一个谓词和一个列表,可以将列表分成两部分:

  • the longest prefix of elements satisfying the predicate满足谓词的元素的最长前缀
  • the remainder of the list列表的其余部分

For example, the Haskell expression …例如,Haskell 表达式……

span (< 10) [2, 2, 2, 5, 5, 7, 13, 9, 6, 2, 20, 4]

… evaluates to … ……评估为……

([2,2,2,5,5,7],[13,9,6,2,20,4])

How does one write the Raku equivalent of Haskell's span function?如何编写与 Haskell 的span函数等效的 Raku?


Update 1更新 1

Based on the answer of @chenyf, I developed the following span subroutine (additional later update reflects negated predicate within span required to remain faithful to the positive logic of Haskell's span function ) …根据@chenyf 的回答,我开发了以下span子例程(稍后的附加更新反映了span内的否定谓词需要保持忠实于Haskell 的span函数逻辑)......

sub span( &predicate, @numberList )
  {
  my &negatedPredicate = { ! &predicate($^x) } ;
  my $idx = @numberList.first(&negatedPredicate):k ;
  my @lst is Array[List] = @numberList[0..$idx-1], @numberList[$idx..*] ;
  @lst ;
  } # end sub span

sub MAIN()
  {
  my &myPredicate = { $_ <= 10 } ;
  my @myNumberList is Array[Int] = [2, 2, 2, 5, 5, 7, 13, 9, 6, 2, 20, 4] ;
  my @result is Array[List] = span( &myPredicate, @myNumberList ) ;

  say '@result is ...' ;
  say @result ;
  say '@result[0] is ...' ;
  say @result[0] ;
  say @result[0].WHAT ;
  say '@result[1] is ...' ;
  say @result[1] ;
  say @result[1].WHAT ;
  } # end sub MAIN

Program output is …程序输出是……

@result is ...
[(2 2 2 5 5 7) (13 9 6 2 20 4)]
@result[0] is ...
(2 2 2 5 5 7)
(List)
@result[1] is ...
(13 9 6 2 20 4)
(List)

Update 2更新 2

Utilizing information posted to StackOverflow concerning Raku's Nil , the following updated draft of subroutine span is …利用发布到StackOverflow上的有关 Raku 的Nil的信息,以下子例程span的更新草案是……

sub span( &predicate, @numberList )
  {
  my &negatedPredicate = { ! &predicate($^x) } ;
  my $idx = @numberList.first( &negatedPredicate ):k ;
  if Nil ~~ any($idx) { $idx = @numberList.elems ; }
  my List $returnList = (@numberList[0..$idx-1], @numberList[$idx..*]) ;
  $returnList ;
  } # end sub span

sub MAIN()
  {
  say span( { $_ == 0 }, [2, 2, 5, 7, 4, 0] ) ;  #  (() (2 2 5 7 4 0))
  say span( { $_ <  6 }, (2, 2, 5, 7, 4, 0) ) ;  #  ((2 2 5) (7 4 0))
  say span( { $_ != 9 }, [2, 2, 5, 7, 4, 0] ) ;  #  ((2 2 5 7 4 0) ())
  } # end sub MAIN

I use first method and :k adverb, like this:我使用first一种方法和:k副词,如下所示:

my @num  = [2, 2, 2, 5, 5, 7, 13, 9, 6, 2, 20, 4];
my $idx = @num.first(* > 10):k;

@num[0..$idx-1], @num[$idx..*];

A completely naive take on this:一个完全天真的看法:

sub split_on(@arr, &pred) {
  my @arr1;
  my @arr2 = @arr;

  loop {
    if not &pred(@arr2.first) {
      last;
    } 

    push @arr1: @arr2.shift
  }

  (@arr1, @arr2);
}

Create a new @arr1 and copy the array into @arr2 .创建一个新的@arr1并将数组复制到@arr2中。 Loop, and if the predicate is not met for the first element in the array, it's the last time through.循环,如果数组中的第一个元素不满足谓词,则为最后一次。 Otherwise, shift the first element off from @arr2 and push it onto @arr1 .否则,将第一个元素从@arr2移出并将其推送到@arr1上。

When testing this:测试时:

my @a = [2, 2, 2, 5, 5, 7, 13, 9, 6, 2, 20, 4];
my @b = split_on @a, -> $x { $x < 10 };

say @b;

The output is:输出是:

[[2 2 2 5 5 7] [13 9 6 2 20 4]]

Only problem here is... what if the predicate isn't met ?这里唯一的问题是......如果不满足谓词怎么办? Well, let's check if the list is empty or the predicate isn't met to terminate the loop.好吧,让我们检查列表是否为空不满足谓词来终止循环。

sub split_on(@arr, &pred) {
  my @arr1;
  my @arr2 = @arr;

  loop {
    if !@arr2 || not &pred(@arr2.first) {
      last;
    } 

    push @arr1: @arr2.shift;
  }

  (@arr1, @arr2);
}

In his presentation 105 C++ Algorithms in 1 line* of Raku (*each) Daniel Sockwell discusses a function that almost answers your question.Raku (*each) 的 1 行* 中的 105 个 C++ 算法的演讲中,Daniel Sockwell 讨论了一个几乎可以回答您的问题的函数。 I've refactored it a bit to fit your question, but the changes are minor.我已经对其进行了一些重构以适应您的问题,但变化很小。

#| Return the index at which the list splits given a predicate.
sub partition-point(&p, @xs) {
    my \zt = @xs.&{ $_ Z .skip };
    my \mm = zt.map({ &p(.[0]) and !&p(.[1]) });
    my \nn = mm <<&&>> @xs.keys;
    return nn.first(?*)
}

#| Given a predicate p and a list xs, returns a tuple where first element is
#| longest prefix (possibly empty) of xs of elements that satisfy p and second
#| element is the remainder of the list.
sub span(&p, @xs) {
    my \idx = partition-point &p, @xs;
    idx.defined ?? (@xs[0..idx], @xs[idx^..*]) !! ([], @xs)
}

my @a = 2, 2, 2, 5, 5, 7, 13, 9, 6, 2, 20, 4;
say span { $_ < 10 }, @a;                #=> ((2 2 2 5 5 7) (13 9 6 2 20 4))
say span { $_ < 5 }, [6, 7, 8, 1, 2, 3]; #=> ([] [6 7 8 1 2 3])

So I figured I'd throw my version in because I thought that classify could be helpful :所以我想我会把我的版本扔进去,因为我认为classify可能会有所帮助:

sub span( &predicate, @list ) { 
    @list
    .classify({
        state $f = True; 
        $f &&= &predicate($_); 
        $f.Int; 
    }){1,0}
    .map( {$_ // []} )
}

The map at the end is to handle the situation where either the predicate is never or always true.最后的map用于处理谓词永远不为真或总是为真的情况。

Version 6.e of raku will sport the new 'snip' function:版本 6.e 的 raku 将采用新的“snip”功能:

use v6.e; 
dd (^10).snip( < 5 );
#«((0, 1, 2, 3, 4), (5, 6, 7, 8, 9)).Seq␤»

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM