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)
Você só pode descrever inclusivo e exclusivo para o fim. Ao descrever a maioria dos cenários, parece um pouco inconsistente.
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 :)
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
fonte
1,5,10,15,20
Lacunas 4, 5, 5, 5 ?!Respostas:
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?
fonte
[1..5[
) confundirá os editores pelo menos tanto quanto a notação de intervalo padrão.!
para excluir o primeiro ou o último elemento pode ser bom.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 ):
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).
fonte
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 expressosrange()
.fonte
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.fonte
[]
,[[
,]]
e][
, sem parêntese ... e nosso padrão é melhor, é claro;)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]
fonte
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.
é claro (1 a MAX) (x => y = (MAX + 1) - x), mas isso é muito comum e não é amigável.
é claro que (0 a 4) (x => y = x * 5) não é tanto clichê. Disputável.
fonte
Que tal algo assim:
O
i
ee
no segundo par de colchetes indica se o início ou o final do intervalo é inclusivo ou exclusivo (ou você pode usarincl
eexcl
se deseja ser mais claro). oby 5
indica 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.fonte
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:
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
fonte
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.
fonte
Ruby tem notação e um objeto Range que funciona. Basicamente, é assim:
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:
Então, aqui estão algumas coisas que você pode querer considerar:
BTW, os intervalos são bastante bons se você permitir que eles sejam incluídos em uma instrução de switch como o Ruby:
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.
fonte
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).
Uma solução que eu certamente consideraria está usando a sobrecarga do operador para permitir, por exemplo,
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.fonte
list expression * 5
também pode significar "repita o valor delist expression
5 vezes", como acontece no Python.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 ...