Stampa concisa di serie matematiche in Raku


9

Serie matematiche, prendiamo ad esempio la sequenza consecutiva rappresentata qui come un 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) };

stampe:

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

a8 =  8
a9 =  9

Le mie domande: 1- Esiste un modo semplice per eliminare solo il primo elemento, ad esempio a0 = 0dall'output stampato?

2- Questo codice potrebbe essere reso più idiomatico?

Grazie.


@DanBron Grazie per il commento. Ho appena pubblicato ed elaborato il post originale.
Lars Malmsteen,

Risposte:


2

Una soluzione barebone

Cominciamo con una soluzione molto semplice per stampare una sintesi di una sequenza. Non si occupa delle specifiche che hai aggiunto alla tua domanda, ma è un buon punto di partenza:

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

Diversamente .kv, che converte il suo invocante nella forma key1, value1, key2, value2, key3, value3, ..., ovvero 6 elementi se il suo invocante contiene 3 elementi, .pairsconverte il suo invocante nella forma key1 => value1, key2 => value2, key3 => value3, ....

Ho usato .pairsinvece in .kvparte perché significava che avrei potuto usare ».gistpiù avanti nel codice per ottenere facilmente una bella key1 => value1visualizzazione per ogni elemento. Lo modificheremo di seguito, ma questo è un buon inizio idiomatico.

Le chiamate .heade .tailsono il modo idiomatico di creare piccoli elenchi del primo e dell'ultimo N elementi da un elenco invocante (purché non sia pigro; ne parleremo più in un attimo).

Data questa soluzione iniziale, say seq-range-gist (0,1 ... Inf)[^10]visualizza:

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

Successivamente, vogliamo essere in grado di "eliminare solo il primo elemento ... dall'output stampato". Sfortunatamente say seq-range-gist (0,1 ... Inf)[1..9]visualizza:

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

Vogliamo che il numero a sinistra di =>mantenga la numerazione della sequenza originale. Per abilitare ciò abbiamo diviso la sequenza sottostante dall'intervallo che vogliamo estrarre. Aggiungiamo un secondo parametro / argomento @rangee aggiungiamo [@range]alla seconda riga del sottotitolo:

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

Ora possiamo scrivere say seq-range-gist (0,1 ... Inf), 1..9per visualizzare:

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

Nella tua domanda hai utilizzato il formato aINDEX = VALUEanziché INDEX => VALUE. Per consentire la personalizzazione dell'essenza, aggiungiamo un terzo &gistparametro / argomento di routine e lo invochiamo invece del .gistmetodo incorporato :

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

Si noti come le invocazioni "metodo" nel corpo del seq-range-gistsub sono ora .&gist, non è .gist. La sintassi .&fooinvoca un sub &foo (che in genere viene invocato scrivendo semplicemente foo), passando l'invocante a sinistra di .come $_argomento al sub.

Nota anche che ho reso il &gistparametro un nome precedendolo con a :.

Quindi ora say seq-range-gist (0,1 ... Inf), 1..9, gist => { "a{.key} = {.value}" }visualizza:

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

Aggiunta di smalto

Il resto di questa risposta è materiale bonus per i lettori che si preoccupano della lucidatura.

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

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

Ops. E anche se ci fossero più coppie della testa e della coda combinate, quindi almeno non abbiamo ottenuto linee ripetute, sarebbe comunque inutile usare l' head, ..., tailapproccio per eludere solo uno o due elementi. Modifichiamo l'ultima istruzione nel corpo secondario per eliminare questi problemi:

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

Successivamente, sarebbe bello se il sub facesse qualcosa di utile se chiamato senza intervallo o sostanza. Possiamo principalmente risolverlo fornendo i parametri @rangee &gisti valori predefiniti adeguati:

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

Se non@seq è pigro , il valore predefinito è l'intero . Se è infinito (nel qual caso è anche pigro), allora il valore predefinito fino a 100 va bene. Ma cosa succede se è pigro ma produce meno di 100 valori definiti? Per coprire questo caso aggiungiamo alla dichiarazione: @range@seq@seq@seq.grep: *.value.defined@pairs

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

Un altro semplice miglioramento sarebbero i parametri opzionali di testa e coda, che porterebbero a una soluzione finale raffinata:

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

La soluzione minima funziona abbastanza bene ed è anche decentemente idiomatica. Nella mia soluzione ho dovuto ricorrere a una variabile 'flag' per fare i conti con la ...parte facendola sembrare più simile a un programma C. Quindi questo risponde davvero ad entrambe le parti della mia domanda. Per quanto riguarda la soluzione "completa", sembra davvero un po 'intimidatoria.
Lars Malmsteen,

Grazie per il tuo feedback e accettando la mia risposta @LarsMalmsteen. Detto questo, ho completamente riscritto la mia risposta e sento che è molto meglio. Ho lasciato cadere la soluzione "completa" - con quello ero andato molto lontano! - ma ho anche riscritto completamente la "soluzione minima" e la spiegazione di accompagnamento. L'ho fatto principalmente per altri lettori successivi, ma potresti ottenere un valore dalla lettura della nuova risposta.
raiph,

7

Puoi saltare i primi N valori su uno Iterable o Sequencecon skip:

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

Se non specifichi un numero, salterà solo un elemento.


Il skipsembra rimuovere solo il ouput cioè l'elemento con l'indice 0th (a0) resti. Ho provato @seq:deletee ha appena sostituito il 0 ° elemento con(Any)
Lars Malmsteen

Infatti. Il skipsarà solo agire come se non esistono gli elementi saltati. Questo può o non può essere quello che vuoi :-)
Elizabeth Mattijsen,

Quando metto la skipin mezzo in modo che sia: for @seq[^10].skip(0).kvessa letteralmente non saltare l'elemento 0th e non importa se do come argomento di skip1 o 2, solo distorce il fuori inoltre. Ho bisogno di un modo pratico per rimuovere il nono elemento da zero.
Lars Malmsteen

1
Forse for @seq[^10].kv.skip(2)è quello che stai cercando?
Elizabeth Mattijsen,

Sì, questo fa il lavoro. In realtà ho provato a mettere il skipdopo il .kvma usando argomenti diversi da 2, quindi non ha funzionato. Grazie per la soluzione
Lars Malmsteen,

7

Questo potrebbe essere un po 'più idiomatico:

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

Non è necessario utilizzare una variabile lessicale all'interno della sequenza; le variabili segnapostoWhatever o possono essere tranquillamente utilizzate all'interno di sequenze. Quindi puoi semplicemente selezionare gli elementi della sequenza che desideri stampare. Che ritorna«(0 1 2 3)(7 8 9 10)␤»


Grazie per la risposta. L' whateveroperatore sta aggiornando, ma l'output di serie / sequenza non risolve il problema principale. Vorrei stampare le serie così come sono visualizzate sui libri di testo matematici, ovvero con una ...notazione in mezzo.
Lars Malmsteen,

@LarsMalmsteen, OK, lo modificherò
jjmerelo il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.