Imprimir series matemáticas de forma concisa en Raku

9

Serie matemática, tome por ejemplo la secuencia consecutiva representada aquí como una matriz:

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

Huellas dactilares:

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

a8 =  8
a9 =  9

Mis preguntas: 1- ¿Hay una manera simple de eliminar solo el primer elemento, es decir, a0 = 0de la salida impresa?

2- ¿Podría este código hacerse más idiomático?

Gracias.

Lars Malmsteen
fuente
@DanBron Gracias por el comentario. Acabo de editar y elaborar la publicación original.
Lars Malmsteen

Respuestas:

2

Una solución básica

Comencemos con una solución muy simple para imprimir la esencia de una secuencia. No trata los detalles que ha agregado a su pregunta, pero es un buen punto de partida:

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

A diferencia .kv, lo que convierte su invocante en la forma key1, value1, key2, value2, key3, value3, ..., es decir, 6 elementos si su invocante contiene 3 elementos, .pairsconvierte su invocante en la forma key1 => value1, key2 => value2, key3 => value3, ....

Lo usé en .pairslugar de en .kvparte porque significaba que podría usarlo ».gistmás adelante en el código para obtener sin esfuerzo una buena key1 => value1visualización de cada elemento. Lo modificaremos a continuación, pero este es un buen comienzo idiomático.

Las llamadas .heady .tailson la forma idiomática de crear pequeñas listas de los primeros y últimos N elementos de una lista de invocadores (siempre que no sea vago; más sobre eso en un momento).

Dada esta solución inicial, say seq-range-gist (0,1 ... Inf)[^10]muestra:

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

A continuación, queremos poder "soltar solo el primer elemento ... de la salida impresa". Desafortunadamente say seq-range-gist (0,1 ... Inf)[1..9]muestra:

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

Queremos que el número a la izquierda del =>retenga la numeración de la secuencia original. Para habilitar esto, dividimos la secuencia subyacente del rango que queremos extraer. Agregamos un segundo parámetro / argumento @range, y agregamos [@range]a la segunda línea del sub:

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

Ahora podemos escribir say seq-range-gist (0,1 ... Inf), 1..9para mostrar:

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

En su pregunta, utilizó el formato en aINDEX = VALUElugar de INDEX => VALUE. Para permitir la personalización de la esencia, agregamos un tercer &gistparámetro / argumento de rutina e invocamos eso en lugar del .gistmétodo incorporado :

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

Observe cómo las invocaciones de "método" en el cuerpo de seq-range-gistsub son ahora .&gist, no .gist. La sintaxis .&fooinvoca un sub &foo (que generalmente se invoca escribiendo solo foo), pasando el invocante a la izquierda del .como un $_argumento para el sub.

Tenga en cuenta también que he hecho que el &gistparámetro tenga un nombre precediéndolo con un :.

Entonces ahora say seq-range-gist (0,1 ... Inf), 1..9, gist => { "a{.key} = {.value}" }muestra:

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

Agregar esmalte

El resto de esta respuesta es material adicional para los lectores que se preocupan por el esmalte.

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

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

Ups E incluso si hubiera más pares que la cabeza y la cola combinados, por lo que al menos no obtuvimos líneas repetidas, aún sería inútil usar el head, ..., tailenfoque para eludir solo uno o dos elementos. Cambiemos la última declaración en el sub cuerpo para eliminar estos problemas:

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

A continuación, sería bueno que el submarino hiciera algo útil si se llama sin un rango o esencia. Sobre todo que podemos arreglar que al dar a las @rangey &gistlos parámetros por defecto adecuados:

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

Si no@seq es perezoso , el valor predeterminado es el rango completo de . Si es infinito (en cuyo caso también es vago), entonces el valor predeterminado de hasta 100 está bien. Pero, ¿qué pasa si es perezoso pero produce menos de 100 valores definidos? Para cubrir este caso, adjuntamos a la declaración: @range@seq@seq@seq.grep: *.value.defined@pairs

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

Otra mejora simple serían los parámetros opcionales de cabeza y cola, lo que llevaría a una solución final pulida:

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
fuente
La solución mínima funciona bastante bien y también es decentemente idiomática. En mi solución, tuve que recurrir a una variable 'flag' para calcular la ...parte que hace que se parezca más a un programa en C. Entonces esto responde a ambas partes de mi pregunta, de verdad. En cuanto a la solución 'integral', se ve un poco intimidante, realmente.
Lars Malmsteen
Gracias por sus comentarios y por aceptar mi respuesta @LarsMalmsteen. Dicho esto, he reescrito completamente mi respuesta y siento que es mucho mejor. Dejé la solución 'integral', ¡con eso me había alejado mucho! - pero también he reescrito completamente la "solución mínima" y la explicación que la acompaña. Lo hice principalmente para otros lectores posteriores, pero podría obtener algún valor al leer la nueva respuesta.
raiph
7

Puede omitir los primeros valores de N en cualquiera Iterable o Sequencecon skip:

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

Si no especifica un número, se saltará solo un elemento.

Elizabeth Mattijsen
fuente
El skipparece eliminar solo la salida, es decir, el elemento con el índice 0 (a0) permanece. Lo intenté @seq:deletey simplemente reemplacé el elemento 0 con(Any)
Lars Malmsteen
En efecto. El skipsolo actuará como si no existen los elementos omitidos. Esto puede o no ser lo que quieres :-)
Elizabeth Mattijsen
Cuando puse el skipen el medio para que se lea: for @seq[^10].skip(0).kvque , literalmente, no omitir el elemento 0th y no importa si me dan como argumento para skip1 o 2, que sólo distorsiona la salida desde ese momento. Necesito una forma práctica de eliminar el elemento 0 desde cero.
Lars Malmsteen
1
¿Quizás for @seq[^10].kv.skip(2)es lo que estás buscando?
Elizabeth Mattijsen el
Sí, eso hace el trabajo. En realidad intenté poner el skipafter .kvpero usando argumentos distintos de 2, por lo que no funcionó. Gracias por la solución.
Lars Malmsteen
7

Esto podría ser un poco más idiomático:

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

No necesita usar una variable léxica dentro de la secuencia; Las variables de marcador de posiciónWhatever o bien se pueden usar con seguridad dentro de las secuencias. Luego, simplemente puede seleccionar los elementos de la secuencia que desea imprimir. Que vuelve«(0 1 2 3)(7 8 9 10)␤»

jjmerelo
fuente
Gracias por la respuesta. El whateveroperador se está actualizando pero la salida de serie / secuencia no aborda el problema principal. Me gustaría imprimir la serie tal como se ve en los libros de texto de matemáticas, es decir, con ...notación intermedia.
Lars Malmsteen
@LarsMalmsteen, OK, lo
editaré