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.