Drucken mathematischer Reihen in Raku

9

Nehmen Sie zum Beispiel die hier dargestellte aufeinanderfolgende Sequenz als Array:

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

Drucke:

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

a8 =  8
a9 =  9

Meine Fragen: 1- Gibt es eine einfache Möglichkeit, nur das erste Element, dh a0 = 0aus der gedruckten Ausgabe, zu löschen?

2- Könnte dieser Code idiomatischer gemacht werden?

Vielen Dank.

Lars Malmsteen
quelle
@ DanBron Danke für den Kommentar. Ich habe gerade den ursprünglichen Beitrag bearbeitet und ausgearbeitet.
Lars Malmsteen

Antworten:

2

Eine Barebone-Lösung

Beginnen wir mit einer sehr einfachen Lösung zum Drucken eines Kerns einer Sequenz. Es geht nicht um die Details, die Sie Ihrer Frage hinzugefügt haben, aber es ist ein guter Ausgangspunkt:

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

Im Gegensatz dazu .kv, das seinen Invokanten in die Form konvertiert key1, value1, key2, value2, key3, value3, ..., dh 6 Elemente, wenn sein Invokant 3 Elemente enthält,.pairs seinen Invokanten in das Formular key1 => value1, key2 => value2, key3 => value3, ....

Ich habe .pairsstatt .kvteilweise verwendet, weil es bedeutete, dass ich ».gistspäter im Code mühelos eine schöne key1 => value1Anzeige für jedes Element erhalten konnte. Wir werden das unten ändern, aber dies ist ein guter idiomatischer Start.

Die .headund .tail-Aufrufe sind die idiomatische Methode, um kleine Listen der ersten und letzten N Elemente aus einer Invocant-Liste zu erstellen (vorausgesetzt, es ist nicht faul; mehr dazu in einem Mo).

In Anbetracht dieser anfänglichen Lösung werden say seq-range-gist (0,1 ... Inf)[^10]Folgendes angezeigt:

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

Als nächstes möchten wir in der Lage sein, "nur das erste Element ... aus der gedruckten Ausgabe zu löschen". Leider say seq-range-gist (0,1 ... Inf)[1..9]zeigt:

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

Wir möchten, dass die Nummer links von der =>die Nummerierung der ursprünglichen Sequenz beibehält. Um dies zu ermöglichen, teilen wir die zugrunde liegende Sequenz aus dem Bereich auf, den wir extrahieren möchten. Wir fügen einen zweiten Parameter / ein zweites Argument hinzu @rangeund hängen [@range]an die zweite Zeile des Unterabschnitts an:

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

Jetzt können wir schreiben, say seq-range-gist (0,1 ... Inf), 1..9um anzuzeigen:

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

In Ihrer Frage haben Sie aINDEX = VALUEeher das Format als verwendet INDEX => VALUE. Um die Anpassung des Kerns zu ermöglichen, fügen wir einen dritten &gistRoutineparameter / Argument hinzu und rufen diesen anstelle der integrierten .gistMethode auf:

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

Beachten Sie, wie die "Methoden" -Aufrufe im Hauptteil von seq-range-gistsub jetzt .&gistnicht mehr sind .gist. Die Syntax .&fooruft ein Sub auf &foo (das normalerweise durch fooeinfaches Schreiben aufgerufen wird ) und übergibt den Invokanten links von .als $_Argument an das Sub.

Beachten Sie auch, dass ich den &gistParameter benannt habe, indem ich ihm ein vorangestellt habe :.

So say seq-range-gist (0,1 ... Inf), 1..9, gist => { "a{.key} = {.value}" }zeigt nun :

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

Politur hinzufügen

Der Rest dieser Antwort ist Bonusmaterial für Leser, die sich für Politur interessieren.

say seq-range-gist (0, 1, 2, 3), ^3 zeigt an:

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

Hoppla. Und selbst wenn es mehr Paare als Kopf und Schwanz zusammen gäbe, so dass wir zumindest keine wiederholten Linien bekamen, wäre es immer noch sinnlos, head, ..., tailnur ein oder zwei Elemente zu entfernen. Lassen Sie uns die letzte Anweisung im Unterkörper ändern, um diese Probleme zu beseitigen:

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

Als nächstes wäre es schön, wenn das U-Boot etwas Nützliches tun würde, wenn es ohne Reichweite oder Kern aufgerufen würde. Wir können das meistens beheben, indem wir den @rangeund &gistParametern geeignete Standardeinstellungen geben:

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

Wenn @seqes nicht faul ist , wird @rangestandardmäßig der gesamte Bereich von verwendet @seq. Wenn @seqes unendlich ist (in diesem Fall ist es auch faul), ist die Standardeinstellung von bis zu 100 in Ordnung. Aber was ist, wenn @seqes faul ist, aber weniger als 100 definierte Werte liefert? Um diesen Fall abzudecken, fügen wir .grep: *.value.definedder @pairsErklärung bei:

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

Eine weitere einfache Verbesserung wären optionale Kopf- und Schwanzparameter, die zu einer endgültigen polierten Lösung führen:

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)
}
Raiph
quelle
Die Minimallösung funktioniert ganz gut und ist auch anständig idiomatisch. In meiner Lösung musste ich auf eine 'Flag'-Variable zurückgreifen, um mit dem ...Teil zu rechnen, der eher wie ein C-Programm aussieht. Das beantwortet also wirklich beide Teile meiner Frage. Die "umfassende" Lösung sieht wirklich ein bisschen einschüchternd aus.
Lars Malmsteen
Vielen Dank für Ihr Feedback und die Annahme meiner Antwort @LarsMalmsteen. Trotzdem habe ich meine Antwort komplett umgeschrieben und finde es viel besser. Ich habe die 'umfassende' Lösung fallen gelassen - damit war ich weit ins Unkraut gegangen! - aber ich habe auch die "Minimallösung" und die dazugehörige Erklärung komplett umgeschrieben. Ich habe dies hauptsächlich für andere spätere Leser getan, aber Sie könnten etwas Wert darauf legen, die neue Antwort zu lesen.
Raiph
7

Sie können die ersten N Werte auf einem Iterable oder Sequencemit überspringen skip:

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

Wenn Sie keine Nummer angeben, wird nur ein Element übersprungen.

Elizabeth Mattijsen
quelle
Das skipscheint nur die Ausgabe zu entfernen, dh das Element mit dem 0. Index (a0) bleibt übrig. Ich habe es versucht @seq:deleteund es hat gerade das 0. Element durch(Any)
Lars Malmsteen
Tatsächlich. Das skipwird nur so tun, als ob die übersprungenen Elemente nicht existieren. Dies kann oder kann nicht sein, was Sie wollen :-)
Elizabeth Mattijsen
Wenn ich das skipdazwischen setze, so dass es lautet: for @seq[^10].skip(0).kvEs überspringt buchstäblich nicht das 0. Element und es spielt keine Rolle, ob ich skip1 oder 2 als Argument gebe , es verzerrt nur das Out weiter. Ich brauche einen praktischen Weg, um das 0. Element von Grund auf zu entfernen.
Lars Malmsteen
1
Vielleicht for @seq[^10].kv.skip(2)ist das, wonach Sie suchen?
Elizabeth Mattijsen
Ja das macht den Job. Eigentlich habe ich versucht, das skipnach dem zu setzen, .kvaber andere Argumente als 2 verwendet, also hat es nicht funktioniert. Vielen Dank für die Lösung.
Lars Malmsteen
7

Dies könnte etwas idiomatischer sein:

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

Sie müssen keine lexikalische Variable innerhalb der Sequenz verwenden. Entweder Whateveroder Platzhaltervariablen können sicher in Sequenzen verwendet werden. Anschließend können Sie einfach die Elemente der Sequenz auswählen, die gedruckt werden soll. Welches kehrt zurück«(0 1 2 3)(7 8 9 10)␤»

jjmerelo
quelle
Danke für die Antwort. Der whateverBediener erfrischt, aber die Serien- / Sequenzausgabe behebt das Hauptproblem nicht. Ich möchte die Serie so drucken, wie sie in Mathe-Texbüchern zu sehen ist, dh mit ...Notation dazwischen.
Lars Malmsteen
@ LarsMalmsteen, OK, ich werde das bearbeiten
jjmerelo