Imprimindo séries matemáticas de forma concisa em Raku

9

Séries matemáticas, considere, por exemplo, a sequência consecutiva representada aqui como uma 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) };

Impressões:

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

a8 =  8
a9 =  9

Minhas perguntas: 1- Existe uma maneira simples de eliminar apenas o primeiro elemento, ou seja, a0 = 0da saída impressa?

2- Esse código pode ser mais idiomático?

Obrigado.

Lars Malmsteen
fonte
@ DanBron Obrigado pelo comentário. Acabei de editar e elaborar o post original.
Lars Malmsteen

Respostas:

2

Uma solução barebones

Vamos começar com uma solução muito simples para imprimir uma essência de uma sequência. Ele não trata das especificidades que você adicionou à sua pergunta, mas é um bom ponto de partida:

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

Ao contrário .kv, o que converte seu invocante no formulário key1, value1, key2, value2, key3, value3, ..., ou seja, 6 elementos se o invocante contiver 3 elementos, .pairsconverte o invocante no formulário key1 => value1, key2 => value2, key3 => value3, ....

Eu usei em .pairsvez de em .kvparte porque isso significava que eu poderia usar ».gistmais tarde no código para obter uma key1 => value1exibição agradável para cada elemento sem esforço . Vamos modificar isso abaixo, mas este é um bom começo idiomático.

As chamadas .heade .tailsão a maneira idiomática de criar pequenas listas do primeiro e do último N elementos a partir de uma lista invocante (desde que não seja preguiçoso; mais sobre isso em breve).

Dada esta solução inicial, say seq-range-gist (0,1 ... Inf)[^10]exibe:

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

Em seguida, queremos poder "soltar apenas o primeiro elemento ... da saída impressa". Infelizmente say seq-range-gist (0,1 ... Inf)[1..9]exibe:

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

Queremos que o número à esquerda do =>retenha a numeração da sequência original. Para habilitar isso, dividimos a sequência subjacente do intervalo que queremos extrair. Nós adicionamos um segundo parâmetro / argumento @rangee anexamos [@range]à segunda linha do sub:

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

Agora podemos escrever say seq-range-gist (0,1 ... Inf), 1..9para exibir:

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

Na sua pergunta, você usou o formato em aINDEX = VALUEvez de INDEX => VALUE. Para permitir a personalização da essência, adicionamos um terceiro &gistparâmetro / argumento de rotina e chamamos isso em vez do .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 como as invocações de "método" no corpo de seq-range-gistsub são agora .&gist, não .gist. A sintaxe .&foochama um sub &foo (que normalmente é chamado apenas por escrito foo), passando o invocante à esquerda do .como $_argumento para o sub.

Observe também que eu fiz o &gistparâmetro nomeado, precedendo-o com a :.

Então agora é say seq-range-gist (0,1 ... Inf), 1..9, gist => { "a{.key} = {.value}" }exibido:

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

Adicionando polonês

O restante desta resposta é um material bônus para os leitores que se preocupam com o polimento.

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

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

Opa E mesmo que houvesse mais pares do que a cabeça e a cauda combinadas, pelo menos não tivéssemos linhas repetidas, ainda não faria sentido usar a head, ..., tailabordagem para eliminar apenas um ou dois elementos. Vamos alterar a última declaração no sub-corpo para eliminar esses 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)

Em seguida, seria bom se o sub fizesse algo útil se chamado sem um intervalo ou essência. Podemos principalmente corrigir isso, fornecendo os parâmetros @rangee &gistpadrões adequados:

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

Se não@seq for preguiçoso , o padrão será o intervalo completo de . Se for infinito (nesse caso, também é preguiçoso), o padrão de até 100 está bom. Mas e se for preguiçoso, mas produzir menos de 100 valores definidos? Para cobrir este caso, anexamos à declaração: @range@seq@seq@seq.grep: *.value.defined@pairs

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

Outra melhoria simples seria parâmetros opcionais de cabeça e cauda, ​​levando a uma solução final polida:

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
fonte
A solução mínima funciona muito bem e também é decentemente idiomática. Na minha solução, tive que recorrer a uma variável 'flag' para contar com a ...parte, fazendo com que parecesse mais um programa em C. Então, isso responde às duas partes da minha pergunta, realmente. Quanto à solução 'abrangente', parece um pouco intimidadora, na verdade.
Lars Malmsteen
Obrigado pelo seu feedback e por aceitar a minha resposta @LarsMalmsteen. Dito isto, reescrevi totalmente minha resposta e sinto que é muito melhor. Eu deixei cair a solução 'abrangente' - eu tinha ido muito longe com isso! - mas também reescrevi completamente a "solução mínima" e a explicação que o acompanha. Fiz isso principalmente para outros leitores posteriores, mas você pode obter algum valor ao ler a nova resposta.
raiph
7

Você pode pular os primeiros N valores em qualquer um Iterable ou Sequencecom skip:

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

Se você não especificar um número, ele ignorará apenas um elemento.

Elizabeth Mattijsen
fonte
O skipparece remover apenas o ouput ou seja, o elemento com o índice de 0 (A0) restos. Eu tentei @seq:deletee ele simplesmente substituiu o elemento 0 com(Any)
Lars Malmsteen
De fato. O skipirá apenas agir como se os elementos ignorados não existissem. Isto pode ou não pode ser o que quiser :-)
Elizabeth Mattijsen
Quando eu colocar o skipno meio para que ele lê: for @seq[^10].skip(0).kvele literalmente não ignorar o elemento 0 e não importa se eu dou como o argumento para skip1 ou 2, ele só distorce a fora ainda. Eu preciso de uma maneira prática de remover o 0º elemento do zero.
Lars Malmsteen
11
Talvez for @seq[^10].kv.skip(2)seja o que você está procurando?
Elizabeth Mattijsen 13/11/19
Sim, isso faz o trabalho. Na verdade, tentei colocar o skipdepois do, .kvmas usando argumentos diferentes de 2, para que não funcionasse. Obrigado pela solução.
Lars Malmsteen
7

Isso pode ser um pouco mais idiomático:

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

Você não precisa usar uma variável lexical dentro da sequência; quer Whateverou variáveis de marcador de posição pode ser utilizada com segurança dentro de sequências. Depois, você pode simplesmente selecionar os elementos da sequência que deseja imprimir. Que retorna«(0 1 2 3)(7 8 9 10)␤»

jjmerelo
fonte
Obrigado pela resposta. O whateveroperador está atualizando, mas a saída de série / sequência não soluciona o problema principal. Eu gostaria de imprimir as séries como elas são vistas nos manuais de matemática, ou seja, com a ...notação intermediária.
Lars Malmsteen
@LarsMalmsteen, OK, eu vou editar isso
jjmerelo