Chia chuỗi tại các vị trí nhất định


8

Làm thế nào để tôi chia nhỏ một cách độc đáo một chuỗi tại một danh sách các vị trí?

Tôi có gì:

.say for split-at( "0019ABX26002", (3, 4, 8) ); 

sub split-at( $s, @positions )
{
  my $done = 0;

  gather 
  {
    for @positions -> $p
    {
      take $s.substr($done, $p - $done );
      $done = $p;
    }
    take $s.substr( $done, * );
  }
}

cái nào hợp lý Tôi bối rối vì thiếu sự hỗ trợ ngôn ngữ cho việc này mặc dù. Nếu "split on" là một thứ, tại sao không "split at" quá? Tôi nghĩ rằng đây nên là một hoạt động cốt lõi. Tôi có thể viết

.say for "0019ABX26002".split( :at(3, 4, 8) );

Hoặc có lẽ tôi đang nhìn một cái gì đó?

Chỉnh sửa: Một chút điểm chuẩn của những gì chúng ta có cho đến nay

O------------O---------O------------O--------O-------O-------O
|            | Rate    | array-push | holli  | raiph | simon |
O============O=========O============O========O=======O=======O
| array-push | 15907/s | --         | -59%   | -100% | -91%  |
| holli      | 9858/s  | 142%       | --     | -100% | -79%  |
| raiph      | 72.8/s  | 50185%     | 20720% | --    | 4335% |
| simon      | 2901/s  | 1034%      | 369%   | -98%  | --    |
O------------O---------O------------O--------O-------O-------O

Mã số:

use Bench;

my $s = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccddddddddddddddddddddddddddddddddddddefggggggggggggggggggg";
my @p = 29, 65, 69, 105, 106, 107;

Bench.new.cmpthese(1000, {
  holli  => sub { my @ = holli($s, @p); },
  simon => sub { my @ = simon($s, @p); },
  raiph => sub { my @ = raiph($s, @p); },
  array-push => sub { my @ = array-push($s, @p); },
});

#say user($s, @p);


sub simon($str, *@idxs ) {
    my @rotors = @idxs.map( { state $l = 0; my $o = $_ - $l; $l = $_; $o } );
    $str.comb("").rotor( |@rotors,* ).map(*.join(""));
}

sub raiph($s, @p) {
    $s.split( / <?{$/.pos == any(@p)}> / )
}

sub holli( $s, @positions )
{
  my $done = 0;

  gather
  {
    for @positions -> $p
    {
      take $s.substr($done, $p - $done );
      $done = $p;
    }
    take $s.substr( $done, * );
  }
}

sub array-push( $s, @positions )
{
  my $done = 0;
  my @result;

  for @positions -> $p
  {
    @result.push: $s.substr($done, $p - $done );
    $done = $p;
  }
  @result.push: $s.substr( $done, * );

  @result;
}

Vì vậy, đối với điều này bạn đang mong đợi : ("001", "9", "ABX2", "6002")?
Scimon Proctor

Trong trường hợp này, đó sẽ là đầu ra có.
Holli

Nếu bạn đang tìm kiếm tốc độ thô, việc tạo một mảng trả về rõ ràng sẽ nhanh hơn một chút: Tôi nhận được ~ 15k với thu thập / lấy, so với gần 19k với Mảng / đẩy, nhưng điều đó giả định rằng mỗi mục cuối cùng là cần thiết.
user0721090601

Ồ wow, tôi không mong đợi điều đó. Tôi đo chênh lệch tốc độ gần 100% giữa mã ban đầu của tôi và eqiv. mã với một mảng rõ ràng và đẩy. Bất cứ ý tưởng tại sao thu thập là rất chậm?
Holli

2
Trước câu hỏi này, tôi đã thêm một mô-đun: String :: Lĩnh vực . Giao diện của nó hơi khác một chút, nhưng tôi nghĩ nó cũng linh hoạt hơn và hữu dụng hơn trong các tình huống khác.
Elizabeth Mattijsen

Câu trả lời:


9

Cá nhân tôi sẽ chia nó thành một danh sách, sử dụng rotorđể chia danh sách và tham gia kết quả:

"0019ABX26002".comb().rotor(3,1,4,*).map(*.join)

Nếu bạn muốn phân chia tại chức năng (sử dụng các chỉ mục đã cho):

sub split-at( $str, *@idxs ) { 
    my @rotors = @idxs.map( { state $l = 0; my $o = $_ - $l; $l = $_; $o } );
    $str.comb("").rotor( |@rotors,* ).map(*.join("")); 
}

Về cơ bản nếu tôi muốn làm công cụ loại danh sách, tôi sử dụng một danh sách.

Tôi đã đưa ra một phiên bản khác mà tôi thực sự thích từ ý nghĩa lập trình chức năng:

sub split-at( $str, *@idxs ) {
    (|@idxs, $str.codes)
    ==> map( { state $s = 0;my $e = $_ - $s;my $o = [$s,$e]; $s = $_; $o } )
    ==> map( { $str.substr(|$_) } );
}

Nó hoạt động chậm hơn một chút so với cái khác.


2
Ừ Chúng tôi nghĩ giống nhau và gần như cùng một lúc :-)
jjmerelo

Cảm ơn bạn đã nhắc nhở tôi về sự tồn tại của rotor. Trong trường hợp này mặc dù. Bạn đang làm rất nhiều việc cho những gì nên hoạt động đơn giản.
Holli

4

Một chiều:

.say for "0019ABX26002" .split: / <?{ $/.pos ∈ (3,4,8) }> /

hiển thị:

001
9
ABX2
6002

1
Khéo léo. Nhưng khá phức tạp.
Holli

1
Cũng rất chậm. Kiểm tra điểm chuẩn
Holli

1
Chào Holli. Tôi đã nâng cao ý kiến ​​của bạn để báo hiệu sự đồng ý với tất cả họ, bao gồm cả việc chậm. /// Là một giải thưởng an ủi cho cách tiếp cận regex của tôi, người ta có thể thay thế bản gốc == 3|4|8bằng ∈ @posđể cải thiện tốc độ. (Và một số có thể thích cách đọc quá.)
raiph

3

Bởi vì mỗi chuỗi con không phụ thuộc vào nhau, hyper trở thành một tùy chọn.

method split-at(\p) {
  do hyper for (0,|p) Z (|p,self.chars) {
    self.substr: .head, .tail - .head
  }
}

Hoặc ở dạng phụ:

sub split-at(\s, \p) {
  do hyper for (0,|p) Z (|p,s.chars) {
    s.substr: .head, .tail - .head
  }
}

Nhưng chi phí liên quan không đáng giá trừ khi số lượng yếu tố được yêu cầu là cực kỳ - trong các thử nghiệm của tôi, nó chậm hơn khoảng mười lần so với hình thức ngây thơ.


2

Đây là giải pháp tôi sẽ sử dụng:

my method break (Str \s: *@i where .all ~~ Int) {
  gather for @i Z [\+] 0,|@i -> ($length, $start) {
    take s.substr: $start, $length
  }
}

say "abcdefghi".&break(2,3,4)   # "ab","cde","fghi"

Các gather/ takecho phép nó được lười biếng nếu cuối cùng bạn không cần phải sử dụng tất cả trong số họ. Vòng lặp lấy @i( 2,3,4trong ví dụ) và nén nó bằng bộ giảm tốc tầng [\+], thường sẽ tạo ra 2,5,9, nhưng chúng ta chèn 0 để làm cho nó 0,2,5,9đánh dấu các chỉ số bắt đầu của mỗi cái. Điều này cho phép thực tế là một substrhoạt động đơn giản .

Bằng cách biến nó thành methodmột phụ, bạn có thể sử dụng nó giống như bạn muốn (thậm chí bạn có thể đặt tên cho nó splitnếu bạn muốn, việc thêm &sigil có nghĩa là Raku sẽ không bị nhầm lẫn dù bạn muốn chế tạo sẵn hay tùy chỉnh.

Thậm chí, bạn có thể thêm nó trực tiếp vào Str:

use MONKEY-TYPING;   # enable augment
augment class Str {
  multi method split (Str \s: *@i where .all ~~ Int) {
    gather for @i Z [\+] 0,|@i -> ($length, $start) {
      take s.substr: $start, $length
    }
  }
}

say "abcdefghi".split(2,3,4)

Trong trường hợp này, nó cần được xác định multi methodvì đã có nhiều splitphương thức khác nhau . Điều tuyệt vời là vì không ai trong số chúng chỉ được xác định bởi các Intđối số, thật dễ dàng để đảm bảo một đối số tăng cường của chúng ta được sử dụng.

Điều đó nói rằng, gọi nó bằng cách sử dụng phiên bản sigiled trong một từ vựng methodchắc chắn là tốt hơn.


ACk, tôi vừa nhận ra rằng bạn thích một :attham số có tên, tôi sẽ cập nhật để làm điều đó.
user0721090601

Tôi không thích nó mỗi se. Tôi muốn nó có ngôn ngữ. Nó đủ phổ biến. Chúng tôi đã có nửa tá biến thể của split. Một như vậy sẽ là một bổ sung hợp lý, imho.
Holli

Holli: Trên thực tế, tôi nghĩ rằng nó sẽ có ý nghĩa combhơn là trong split, vì lược đã được thiết kế để hoạt động trên các số nguyên. Còn cái này thì sao? tio.run/##VVLJasMwFLz7KwYTgk1dZyn0kODQaw@FQo4lLbIs26LygiTThCy/...
user0721090601

Ngoài ra, bổ sung cốt lõi có thể được thực hiện và có thể được đề xuất trong github.com/Raku/probols-solve . Trong trường hợp này, tôi nghĩ rằng một đề xuất về lược () có thể khá dễ dàng để được chấp thuận, mặc dù nó có thể không được đưa vào cốt lõi cho đến 6.f (không chắc chắn nếu 6.e vẫn mở cho mọi thứ)
user0721090601

Giải pháp của bạn đang mong đợi chiều dài là đầu vào, không phải vị trí.
Holli
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.