Projetando o literal de faixa perfeita


9

Eu tenho pensado em como eu iria projetar o literal "range" perfeito se eu fosse projetar uma linguagem. Para você que não conhece, saiba um literal de intervalo em uma declaração que representa um intervalo de valores, como 1 a 4. Eles são mais comumente usados ​​em loops for / foreach

Parece haver alguns problemas que se deve levar em consideração

  • O suporte para intervalos inclusivos e exclusivos, alinhar com +1 ou -1 aos pontos de extremidade parece um pouco impreciso e propenso a erros.

  • Suporte para pisar, para que você possa fazer um intervalo de números pares ou ímpares, por exemplo

  • Legibilidade, deve ficar evidente o que o literal de intervalo descreve

  • Sem ambiguidade, deve ser perfeitamente sem ambiguidade o que o literal de intervalo descreve

  • O padrão provavelmente deve ser de inclusivo a exclusivo, já que é usado na maioria dos casos para fazer loop sobre matrizes etc.

De qualquer forma, um exemplo de literal de intervalo que eu vi é Ruby, que está na forma de 1..3 para um intervalo exclusivo (no final) e 1 ... 3 para inclusivo (no final). Você também pode executar 1..10.step (5). Após uma análise cuidadosa, encontrei algumas coisas que não gostei sobre essa abordagem (pelo meu conhecimento limitado de ruby)

  1. Você só pode descrever inclusivo e exclusivo para o fim. Ao descrever a maioria dos cenários, parece um pouco inconsistente.

  2. Variando por apenas mais um. parece uma receita para dificultar ver se um intervalo é inclusivo ou exclusivo. Eu não sei sobre você, mas os pontos tendem a se tornar um borrão :)

  3. Adicionar método como notação para intervalos parece misturar a noção de literal com a de uma classe que parece um pouco inconsistente (mesmo que os intervalos sejam compilados em uma classe)

De qualquer forma, depois de ponderar diferentes alternativas. Eu vim com isso

  • [5..1] 5,4,3,2,1
  • [1..5 [ 1,2,3,4
  • ] 1..5] 2,3,4,5
  • [ 0..5..20] 0,5,10,15,20

e assim por diante. Eu gosto disso porque [normalmente desonra um conjunto e isso meio que se encaixa nisso, mesmo que isso em contraste com um conjunto seja ordenado.

Uma coisa que me deixa um pouco impressionado é tornar obrigatórios ou não os indicadores exclusivos / inclusivos, ou seja, se você escrever apenas 1..5, o padrão será 1,2,3,4, já que é o caso mais comum de matrizes, etc. É mais fácil e legível, mas menos específico e, se você tiver que escrever [1..5 [, aprenderá cedo sobre como eles funcionam.

Então, o que você acha, eu cobri a maioria das bases, negligencio alguma coisa? você tornaria o [] obrigatório? Você projetaria literais de intervalo de maneira diferente em sua linguagem de programação?

Candidatos

  • estilo de colchete: [0..10 [ , com etapa: [0..5..20 [
  • notação de intervalo: [0..10) com etapa: [0..5..20)
  • exclamação por excludente. 0 ..! 10, com passo: 0..5 ..! 20
    • com passo diferente. 0 ..! 20, 5
    • no entanto, isso tornaria o padrão * 0..10 ' inclusivo inclusive
  • prolixo: [0 a! 20 por 5]

Devo dizer que o meu favorito esteticamente até agora é 0 ... 10 e 0..5 ..! 20 , só desejo que o padrão 0..10 para inclusivo-exclusivo seja mais lógico


2
1,5,10,15,20Lacunas 4, 5, 5, 5 ?!
Peter Taylor

oops :) corrigido
Homde

Respostas:


14

Por que inventar o que já existe em Matemática?

Notação de intervalo

Isso deve cobrir o seu ponto de 'ambiguidade', como ele já existe.

Seu único problema seria definir o passo. Talvez algo mais em matemática possa ajudar com isso?


3
Oi, eu tenho realmente sobre notação de intervalo. No entanto, o que me fez não querer usá-lo foi o fato de não ser simétrico com [e) e usar parênteses, além de não ter etapas (como você mencionou). Usar parênteses parece ter o potencial de confundir a correspondência entre chaves e eu prefiro salvá-las em expressões. Usar] e [é um padrão iso e parecia melhorar com a integração de etapas, mas essa é apenas minha opinião, é claro.
Homde

7
@MKO: Sua idéia de usar colchetes inigualáveis ​​(como [1..5[) confundirá os editores pelo menos tanto quanto a notação de intervalo padrão.
precisa

2
Hmm, outra ideia, que tal usar! para, ou seja:!! 1 .. 5 ou 0..5 .. 20 (com stepping)
Homde

11
@ MKO: Usar !para excluir o primeiro ou o último elemento pode ser bom.
FrustratedWithFormsDesigner

8

Você já viu faixas Haskell? A ideia deles de etapas é semelhante à sua (no meio), mas indicada com uma diferença sintática ( da resposta de @ luqui ):

[1,2..10] = [1,2,3,4,5,6,7,8,9,10]
[1,3..10] = [1,3,5,7,9]
[4,3..0]  = [4,3,2,1,0]
[0,5..]   = [0,5,10,15,20,25,30,35...  -- infinite
[1,1..]   = [1,1,1,1,1,1,1,1,1,1,1...  -- infinite

Acho essa notação muito intuitiva: você começa a enumerar e pula o corpo para marcar onde deve terminar.

Ele não cobre o caso de "exclusivo", mas, francamente, prefiro ser claro sobre o comportamento uma vez (especifique qual limite é inclusivo e qual é exclusivo) e espera que o usuário faça ajustes, a menos que a sintaxe seja extremamente clara (e não confunde editores independentes de idioma).


5

Você deve ler sobre a compreensão da lista nos idiomas que as oferecem.

O Python, por exemplo, cobre seus casos muito bem com a range()função e a compreensão da lista para casos muito complexos para serem expressos range().


2

Acredito que sua anotação está longe de ser inequívoca. Intuitivamente, [0..5..20]não me parece um intervalo com largura de passo = 5. Até falha em alguns cantos: que tal expressar o conjunto (0,2) - [0..2..2]ou o que devo colocar no meio?

Eu acho que a notação de fatia do Python é uma abordagem sólida: [start:end:step](passo sendo opcional).

Se você realmente precisar de suporte para intervalos exclusivos e inclusivos, eu usaria a notação de intervalo padrão (ou seja, use (e [), a menos que isso introduz ambiguidades sintáticas no seu idioma.


11
sobre a notação "standard", escolas europeias ensinar [], [[, ]]e ][, sem parêntese ... e nosso padrão é melhor, é claro;)
Matthieu M.

@ Matthieu: Na verdade, aqui na Holanda (que fica na Europa), também aprendemos essa notação padrão.
Frits

1

Acho que quando você está definindo um terceiro valor de incremento, é melhor adicionar esse valor ao final do que ao meio, já que esse é um valor opcional e parece mais intuitivo para programadores experientes do que adicionar um terceiro argumento opcional no meio .

então, em vez de [1..5..20], você poderia fazer algo como [1..20] [5] ou [1..20,5]


Esse é um ponto válido e, talvez, uma questão de gosto, parecia-me que seria mais fácil pensar em "1 passo 5 a 20" em vez de "de 1 a 2, com o passo 5" usando [1..20] [5] parece um pouco confuso, mas talvez a abordagem por vírgula seja viável. Eu apreciaria mais feedback sobre o que
Homde

1
  • [1..5 [1,2,3,4
  • ] 1..5] 2,3,4,5
  • ] 1..5 [2,3,4

Por que não usar apenas [1..4], [2..5], [2..4]? As escalas excluídas não oferecem muitos benefícios. Você não precisa escrever MIN + 1 ou MAX-1, sim, mas eles não o proíbem de qualquer maneira. Muita variação, imho. Minha linguagem de escolha, scala, tem (1 a 3) e (1 a 3) [= (1 a 2)], mas isso me confundiu no começo. Agora eu sempre uso 'x para y' e, portanto, sei que a opção que eu não uso é a que exclui, mas é claro que não me beneficio de uma opção que não uso.

  • [5..1] 5,4,3,2,1

é claro (1 a MAX) (x => y = (MAX + 1) - x), mas isso é muito comum e não é amigável.

  • [0..5..20] 0,5,10,15,20

é claro que (0 a 4) (x => y = x * 5) não é tanto clichê. Disputável.


O principal benefício da exclusão de intervalos, após um aumento, é que o número de elementos é a diferença dos terminais.
Justin L.

@JustinL .: 5-1 é 4 na minha álgebra, mas contém 3 elementos, excluindo os limites: 2, 3, 4.
user unknown

1

Que tal algo assim:

  • [5..1] 5,4,3,2,1
  • [1..5] [i, e] 1,2,3,4
  • [5..1] [i, e] 5,4,3,2
  • [1..5] [e, i] 2,3,4,5
  • [1..5] [e, e] 2,3,4
  • [0..20] por 5 0,5,10,15,20

O ie eno segundo par de colchetes indica se o início ou o final do intervalo é inclusivo ou exclusivo (ou você pode usar incle exclse deseja ser mais claro). o by 5indica o intervalo de passos. O primeiro e o último exemplo incluem o primeiro e o último valor, então eu omiti o[i,i] , que acho que seria um comportamento padrão assumido OK.

Os valores padrão podem ser [i,i]para o especificador inclusivo / exclusivo eby 1 para o especificador de etapa.

Normalmente, eu sugeriria a notação padrão envolvendo (e [como mencionado por @Dan McGrath, mas concordo que no código isso pode parecer confuso.


Alguém pode explicar o voto negativo?
FrustratedWithFormsDesigner

Não vi nada claramente errado . Eu votei para neutralizar o voto negativo.
Berin Loritsch

1

E o vencedor é

Desculpe por escolher minha própria solução, eu realmente aprecio todos os comentários e feedback e, por favor, critique esta solução se você encontrar algo errado com ela

Então eu decidi ir com:

0..5           = 0,1,2,3,5
0..5..10       = 0,5,10
..3            = 0,1,3
!0..!5         = 1,2,3,4
3..            = 3 to infinity

segmented ranges
-
0..5,..5..20   = 0,1,2,3,4,5,10,15,20
0..3,10..5..20 = 0,1,2,3,10,15,20

Como você pode ver, inclusivo é o padrão nos dois sentidos, é o que faz mais sentido intuitivamente, especialmente ao usar as etapas. Você pode usar ! para tornar um fim exclusivo.

A desvantagem é que normalmente você deseja usar exclusivo ao trabalhar com uma matriz, mas, novamente, na maioria das vezes você provavelmente deve usar algo como for-each nesses casos. Se você realmente precisar trabalhar com o índice, o analisador poderá reconhecer quando você esquecer o marcador de exclusão no final e emitir um aviso

Eu continuei usando .. em ambos os lados da variável step em vez de usar vírgula ou qualquer outra coisa, já que era o que parecia mais limpo e espero que seja o mais legível.

Também introduzi alguma sintaxe com vírgulas para poder fazer intervalos em que partes diferentes têm etapas diferentes


Parece bom, exceto ao especificar a etapa. Eu acho que seria mais limpo como [0..10 por 5]. O intervalo segmentado pode ser [0..5, ..20 por 5], etc. Também é possível especificar [números do primeiro passo a passo], os quais (diferente das outras formas) podem permitir um tamanho de passo zero.
supercat 16/02

0

Eu não usaria o formulário '[1..5 [' pelos mesmos motivos que você evitaria misturar parênteses e chaves - isso confundirá a correspondência entre chaves da maioria dos editores existentes. Talvez use um marcador dentro do aparelho, como '[1 ​​.. | 5]'.

A outra coisa a considerar é o comportamento dos métodos para obter os limites. Por exemplo, 'begin' / 'end' vs. 'first' / 'last' vs. 'min' / 'max'. Eles precisam ser sãos para limites inclusivos e exclusivos, bem como aqueles com um tamanho de etapa.


0

Ruby tem notação e um objeto Range que funciona. Basicamente, é assim:

1..5  # range 1,2,3,4,5

Com Ruby, o tamanho da etapa não é uma função do intervalo, mas uma função de iteração no intervalo. Poderíamos usar o mesmo intervalo acima e iterá-lo de maneira diferente se quiséssemos:

(1..5).step(3) { |x| puts x }
(1..5).step(2) { |x| puts x }

Então, aqui estão algumas coisas que você pode querer considerar:

  • Adicionar suporte ao idioma para terminais inclusivos / exclusivos pode ser problemático. Se todos os intervalos forem inclusivos (escolha de Ruby), os desenvolvedores ajustarão os pontos finais do intervalo de acordo. Como você representa a inclusão ou exclusividade dos pontos de extremidade de uma maneira visualmente inequívoca e inequívoca com uma gramática simples do analisador?
  • Você está usando o intervalo para mais do que apenas iterar? Exemplos comuns incluem comparações de intervalos, emendas, valor em comparações de intervalos. Se sim, como você representa essas possibilidades? (Consulte o link para a classe Range para obter idéias para cada um desses exemplos.)

BTW, os intervalos são bastante bons se você permitir que eles sejam incluídos em uma instrução de switch como o Ruby:

switch(myval)
    when 0..33 then puts "Low"
    when 34..66 then puts "Medium"
    when 67..100 then puts "High"
end

Não estou dizendo que o objeto de alcance do Ruby seja o fim e o tudo, mas é uma implementação funcional do conceito sobre o qual você está falando. Brinque com ele para ver o que você gosta, não gosta e faria diferente.


Leia a questão cuidadosamente, OP é ciente de gamas de Ruby:Anyways, one example of range literal I've seen is Ruby which is in the form of 1..3 for an exclusive (on the end) range and 1...3 for inclusive (on the end). You can also do 1..10.step(5).
FrustratedWithFormsDesigner

0

Uma solução que eu certamente consideraria está usando a sobrecarga do operador para permitir, por exemplo,

1+[0..3]*5

Isso não seria necessariamente transparente para todos, mas uma vez que eles parem e pensem que espero que a maioria dos programadores entenda isso, e as pessoas que usam a linguagem regularmente simplesmente a aprendem como um idioma.

Para esclarecer, o resultado seria [1, 6, 11, 16]levantar a multiplicação e depois a adição ao longo do intervalo. Eu não tinha percebido que isso poderia ser ambíguo para os usuários de Python; Penso em intervalos como subconjuntos de pedidos totais e não como listas. e repetir um conjunto não faz sentido.


2
Muito ambíguo. list expression * 5também pode significar "repita o valor de list expression5 vezes", como acontece no Python.
Nikie 04/04

Isso é muito estranho e eu não tenho certeza do que isso seria ... 1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3? 1,3,6,9,12? 5,10,15? outra coisa, talvez? De qualquer maneira, acho essa notação completamente contra-intuitiva. Parece que talvez seja algum tipo de operação ponteiro C estranho ...
FrustratedWithFormsDesigner
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.