Zwięzłe drukowanie serii matematycznych w Raku


9

Szeregi matematyczne, weźmy na przykład kolejną sekwencję przedstawioną tutaj jako tablicę:

my @seq = my $a=0, {++$a} ... *;
for @seq[^10].kv {state $f=0; ($^k < 4 or $^k > 7) ?? say "a$^k =  "  ~ $^v !! (say "..." if $f ne 1; $f=1) };

Wydruki:

a0 =  0
a1 =  1
a2 =  2
...

a8 =  8
a9 =  9

Moje pytania: 1 - Czy istnieje prosty sposób na usunięcie tylko pierwszego elementu, tj. a0 = 0Z wydruku?

2- Czy ten kod może być bardziej idiomatyczny?

Dziękuję Ci.


@DanBron Dziękuję za komentarz. Właśnie zredagowałem i opracowałem oryginalny post.
Lars Malmsteen,

Odpowiedzi:


2

Rozwiązanie typu barebones

Zacznijmy od bardzo prostego rozwiązania do drukowania streszczenia sekwencji. Nie zajmuje się szczegółami dodanymi do pytania, ale jest dobrym punktem wyjścia:

sub seq-range-gist ( @seq ) {
  my @pairs = @seq.pairs;
  join "\n", @pairs.head(3)».gist, '...', @pairs.tail(2)».gist
}

W przeciwieństwie do tego .kv, który konwertuje swojego wywoływacza do postaci key1, value1, key2, value2, key3, value3, ..., tj. 6 elementów, jeśli jego wywoływacz zawiera 3 elementy, .pairsprzekształca swojego wywoływacza do postaci key1 => value1, key2 => value2, key3 => value3, ....

Użyłem .pairszamiast .kvczęściowo, ponieważ oznaczało to, że mogłem użyć ».gistpóźniej w kodzie, aby bez wysiłku uzyskać ładny key1 => value1wyświetlacz dla każdego elementu. Zmodyfikujemy to poniżej, ale to dobry początek idiomatyczny.

.headI .tailpołączenia są idiomatyczne sposób na tworzenie małych list pierwszych i ostatnich n elementów z listy invocant (pod warunkiem, że nie jest leniwy, więcej na ten temat w MO).

Biorąc pod uwagę to wstępne rozwiązanie, say seq-range-gist (0,1 ... Inf)[^10]wyświetla:

0 => 0
1 => 1
2 => 2
...
8 => 8
9 => 9

Następnie chcemy móc „upuścić tylko pierwszy element ... z wydruku”. Niestety say seq-range-gist (0,1 ... Inf)[1..9]wyświetla:

0 => 1
1 => 2
2 => 3
...
7 => 8
8 => 9

Chcemy, aby liczba po lewej stronie =>zachowała numerację oryginalnej sekwencji. Aby to umożliwić, rozdzieliliśmy leżącą u podstaw sekwencję z zakresu, który chcemy wyodrębnić. Dodajemy drugi parametr / argument @rangei dołączamy [@range]do drugiego wiersza sub:

sub seq-range-gist ( @seq, @range ) {
  my @pairs = @seq.pairs[@range];

Teraz możemy napisać say seq-range-gist (0,1 ... Inf), 1..9do wyświetlenia:

1 => 1
2 => 2
3 => 3
...
8 => 8
9 => 9

W swoim pytaniu użyłeś formatu aINDEX = VALUEzamiast INDEX => VALUE. Aby umożliwić dostosowanie GIST, dodajemy trzeci &gistrutynowy parametr / argument i wywołujemy go zamiast wbudowanej .gistmetody:

sub seq-range-gist ( @seq, @range, :&gist ) {
  my @pairs = @seq.pairs[@range];
  join "\n", @pairs.head(3)».&gist, '...', @pairs.tail(2)».&gist
}

Zauważ, że seq-range-gistteraz .&gistnie są wywołania „metody” w treści sub .gist. Składnia .&foowywołuje podrzędny &foo (który jest zwykle wywoływany przez napisanie po prostu foo), przekazując wywoływacza po lewej stronie .jako $_argument do podrzędnego.

Zauważ też, że nadałem &gistparametrowi nazwę, poprzedzając go znakiem :.

Teraz say seq-range-gist (0,1 ... Inf), 1..9, gist => { "a{.key} = {.value}" }wyświetla:

a1 =  1
a2 =  2
a3 =  3
...
a8 =  8
a9 =  9

Dodawanie polskiego

Pozostała część tej odpowiedzi jest dodatkowym materiałem dla czytelników, którym zależy na języku polskim.

say seq-range-gist (0, 1, 2, 3), ^3 wyświetla:

0 => 0
1 => 1
2 => 2
...
1 => 1
2 => 2

Ups I nawet gdyby było więcej par niż głowa i ogon łącznie, więc przynajmniej nie otrzymywaliśmy powtarzających się linii, nadal byłoby bezcelowe stosowanie head, ..., tailpodejścia, aby uniknąć tylko jednego lub dwóch elementów. Zmieńmy ostatnią instrukcję w treści podrzędnej, aby wyeliminować następujące problemy:

  join "\n",
    @pairs < $head + $tail + 3   # Of course, the 3 is a bit arbitrary
      ?? @pairs».&gist
      !! (@pairs.head($head)».&gist, '...', @pairs.tail($tail)».&gist)

Następnie byłoby miło, gdyby subwoofer zrobił coś pożytecznego, gdyby został wywołany bez zasięgu lub istoty. W większości przypadków możemy to naprawić, nadając parametrom @rangei &gistodpowiednie wartości domyślne:

sub seq-range-gist (
  @seq,
  @range = @seq.is-lazy ?? ^100 !! ^@seq,
  :&gist = { .gist }
) {

Jeśli nie@seq jest leniwy , domyślnie jest to pełny zakres . Jeśli jest nieskończony (w takim przypadku jest również leniwy), to domyślnie do 100 jest w porządku. Ale co, jeśli jest leniwy, ale daje mniej niż 100 zdefiniowanych wartości? Aby uwzględnić tę sprawę, dołączamy do deklaracji: @range@seq@seq@seq.grep: *.value.defined@pairs

  my @pairs = @seq.pairs[@range].grep: *.value.defined;

Kolejnym prostym ulepszeniem byłyby opcjonalne parametry głowy i ogona, prowadzące do ostatecznego dopracowanego rozwiązania:

sub seq-range-gist (
  @seq,
  @range = @seq.is-lazy ?? ^100 !! ^@seq,
  :$head = 3,
  :$tail = 2,
  :&gist = { .gist }
) {
  my @pairs = @seq.pairs[@range].grep: *.value.defined;
  join "\n",
    @pairs <= $head + $tail + 2
      ?? @pairs».&gist
      !! (@pairs.head($head)».&gist, '...', @pairs.tail($tail)».&gist)
}

Minimalne rozwiązanie działa całkiem dobrze, a także jest przyzwoicie idiomatyczne. W moim rozwiązaniu musiałem skorzystać ze zmiennej „flag”, aby rozliczyć się z ...częścią, dzięki czemu wygląda bardziej jak program w języku C. To naprawdę odpowiada na obie części mojego pytania. Jeśli chodzi o „kompleksowe” rozwiązanie, wygląda naprawdę onieśmielająco.
Lars Malmsteen,

Dziękuję za twoją opinię i akceptując moją odpowiedź @ LarsMalmsteen. To powiedziawszy, całkowicie przepisałem moją odpowiedź i uważam, że jest znacznie lepiej. Porzuciłem „kompleksowe” rozwiązanie - poszedłem z tym daleko w chwasty! - ale całkowicie przepisałem „minimalne rozwiązanie” i towarzyszące mu wyjaśnienie. Zrobiłem to głównie dla innych późniejszych czytelników, ale możesz odnieść korzyści z przeczytania nowej odpowiedzi.
raiph

7

Możesz pominąć pierwsze N ​​wartości na dowolnym Iterable lub za Sequencepomocą skip:

for (^5).skip(3) {
    .say
}
# 3
# 4

Jeśli nie podasz liczby, pominie tylko jeden element.


skipWydaje się usunąć tylko ouput czyli element z indeksem 0th (a0) pozostaje. Próbowałem @seq:deletei właśnie zastąpiłem 0 element(Any)
Lars Malmsteen

W rzeczy samej. skipBędzie po prostu działać tak, jakby pominięte elementy nie istnieją. To może być lub nie być to, czego chcesz :-)
Elizabeth Mattijsen

Kiedy umieścić skippomiędzy tak, że brzmi: for @seq[^10].skip(0).kvto dosłownie nie pominąć elementu 0TH i to nie ma znaczenia, czy dam jako argument do skip1 lub 2, po prostu wypacza się ponadto. Potrzebuję praktycznego sposobu na usunięcie 0 elementu od podstaw.
Lars Malmsteen

1
Być może for @seq[^10].kv.skip(2)jest to, czego szukasz?
Elizabeth Mattijsen,

Tak, to działa. Właściwie próbowałem wstawić skippo tym, .kvale używając argumentów innych niż 2, więc to nie zadziałało. Dziękuję za rozwiązanie.
Lars Malmsteen,

7

Może to być nieco bardziej idiomatyczne:

my @seq = 0, *+1 ... *;
say @seq[^4], @seq[7..10]

Nie musisz używać zmiennej leksykalnej w sekwencji; albo Whateverczy zmienne zastępcze mogą być bezpiecznie stosowane w sekwencjach. Następnie możesz po prostu wybrać elementy sekwencji, które chcesz wydrukować. Który zwraca«(0 1 2 3)(7 8 9 10)␤»


Dziękuję za Twoją odpowiedź. whateverOperator refreshening ale wyjście seria / sekwencja nie rozwiązuje głównego problemu. Chciałbym wydrukować serię tak, jak są one widoczne w podręcznikach matematyki, tj. Z ...zapisem pomiędzy nimi.
Lars Malmsteen,

@ LarMalmsteen, OK,
zmienię
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.