Julia: mini julep: se x então y

Criado em 16 mai. 2016  ·  53Comentários  ·  Fonte: JuliaLang/julia

Em várias discussões, foi sugerido permitir sintaxe como:

if x then y

Como uma declaração "if" de forma abreviada e como alternativa ao comum:

x && y

sintaxe que aproveita o operador && de curto-circuito para executar condicionalmente y (com y geralmente contendo outros efeitos colaterais e não necessariamente retornando um Bool ).

As principais vantagens desta construção if-then são: código mais legível, menos dependente do abuso de && e formalmente incluindo um formulário de declaração "if" que não requer um end palavra-chave.

Ocorreu-me outro dia que essa sintaxe também forneceria um meio conveniente para implementar o #550, que seria assim:

A = [if x % 2 == 0 then f(x) for x in 1:10]

Contando com o fato de que if-then não requer uma palavra-chave end , que provavelmente precisaríamos de alguma forma, mesmo se fôssemos com guardas no estilo python:

A = [f(x) for x in range(10) if x % 2 == 0]

Para ser claro, a sintaxe de guarda de Julia estaria essencialmente fazendo uma reescrita de:

A = [if x % 2 == 0 then f(x) for x in 1:10]

para

A = [Filter(x->x % 2 == 0, f(x) for x in 1:10)]

Também como uma nota esclarecedora, isso permitiria a sintaxe de guarda no nível _generator_ em oposição apenas ao nível _comprehension_ (que corresponde ao que o python permite também).

julep

Comentários muito úteis

Talvez eu esteja em minoria, já que outras linguagens adotaram essa abordagem, mas sempre achei a ação que vem antes da condição muito estranha, por exemplo, println("positive") if x > 0 . Eu acho que é mais conciso do que if x > 0 println("positive") end mas pelo menos para mim tem o custo de legibilidade.

Eu penso nisso como uma conversa:

Julia: "Vou imprimir uma string..."
Haroldo: "Incrível!"
Julia: "...mas somente se alguma condição for atendida."
Haroldo: "Ah. :("

contra

Julia: "Se alguma condição for atendida, vou imprimir uma string."
Harold: "Ok, legal."

Todos 53 comentários

A sintaxe do modificador if/for no estilo Perl/Ruby parece que combinaria melhor com isso. Em outras palavras:

println("positive") if x > 0         # conditional execution
x^2 for x=1:100                      # generator
[ x^2 for x=1:100 ]                  # comprehension
x^2 if x % 3 == 0 for x = 1:100      # filtered generator
[ x^2 if x % 3 == 0 for x = 1:100 ]  # filtered comprehension

Ele também tem o benefício de ter precedentes em outras linguagens, que a sintaxe if - then sem end parece não ter.

Apenas uma observação lateral sobre gerenciamento de problemas: #6823 discutiu exatamente esse problema. Em algum momento foi fechado sem comentários, continuei perguntando por que foi fechado porque claramente não havia um consenso para fechá-lo, mas nunca obtive resposta. Parece que seria mais eficiente não encerrar questões como a #6823, caso contrário, essas discussões apenas andarão em círculos e todos os pontos da edição original serão repetidos novamente. Provavelmente faria sentido mudar o título do #6823 em algum momento e depois adicionar isso aqui.

Sim, bom ponto. De fato, o nº 6823 parece estar no meio da discussão e provavelmente não deveria ter sido encerrado.

Talvez eu esteja em minoria, já que outras linguagens adotaram essa abordagem, mas sempre achei a ação que vem antes da condição muito estranha, por exemplo, println("positive") if x > 0 . Eu acho que é mais conciso do que if x > 0 println("positive") end mas pelo menos para mim tem o custo de legibilidade.

Eu penso nisso como uma conversa:

Julia: "Vou imprimir uma string..."
Haroldo: "Incrível!"
Julia: "...mas somente se alguma condição for atendida."
Haroldo: "Ah. :("

contra

Julia: "Se alguma condição for atendida, vou imprimir uma string."
Harold: "Ok, legal."

@ararslan : Eu não discordo, que é uma das razões pelas quais esse recurso não está em Julia, apesar da arte anterior em Ruby e Perl. No entanto, ele se mistura muito melhor com a sintaxe do modificador for para geradores e compreensões, e é por isso que eu trouxe isso aqui.

@StefanKarpinski Sim, concordo, combina melhor com eles. Ainda estou irremediavelmente apegado às condições que precedem as ações. :smile: (Mas como eu me peguei dizendo muitas vezes recentemente, minha opinião realmente não importa; eu sou apenas um cara.)

Suponho que se você tivesse algo como

if x % 3 == 0 then x^2 for x = 1:100

então não fica imediatamente claro que você está iniciando uma compreensão/gerador/coisaamajig, porque também lê como se pudesse ser agrupado como

if x % 3 == 0 then (x^2 for x = 1:100)

ou seja, se alguma condição então gerador. eu não me importaria

if x % 3 == 0 x^2 end for x = 1:100            # More obvious what's happening, IMO
filter(x -> x % 3 == 0, [x^2 for x = 1:100])   # Verbose, but... ¯\_(ツ)_/¯

Acho que se a condição fosse seguir a ação em um gerador, acho que preferiria uma palavra-chave diferente de if , por exemplo where . Então ele lê quase como uma consulta SQL, por exemplo

x^2 for x = 1:100 where x % 3 == 0

Acho que where deixa um pouco mais claro que você está filtrando os valores de x produzidos por for do que if .

Parece-me melhor ter o fluxo de dados em uma única direção, embora neste caso da direita para a esquerda em vez da esquerda para a direita.

Você pode detalhar o que você quer dizer com isso?

O for 1:n "gera" valores, o if x % 3 == 0 os filtra e o x^2 os transforma – os geradores já fluem da direita para a esquerda e para manter esse fluxo o filtro precisa ir no meio. Se a cláusula if for para a direita da cláusula for , os dados "fluirão" do meio para a extrema direita e depois para a extrema esquerda, o que é estranho. Se a cláusula if estiver à esquerda, os dados "fluirão" da extrema direita para a extrema esquerda para o meio. Eu sei que o SQL coloca a cláusula where à direita dos nomes das tabelas e as expressões à esquerda – porque isso se parece mais com inglês – mas sempre achei isso chato de ler e acho que ler como inglês não é uma heurística que deve ser levada longe demais no design de linguagens de programação.

Coisas que eu não gosto em if x then y :

  1. Ele apresenta a nova palavra-chave then .
  2. É mais curto escrever if x y end do que if x then y ; x && y é ainda mais curto.
  3. A palavra-chave then é amplamente usada em linguagens para o tipo exatamente oposto de sintaxe: uma sintaxe if várias linhas que requer uma palavra-chave end (ou fi no bash , que nojo).

Oh sim, eu entendo o que você quer dizer agora sobre o fluxo. Isso faz sentido. Obrigada pelo esclarecimento!

Seria estranho poder fazer

x^2 if x % 3 == 0 for x = 1:100

e _não_ poder fazer

println("positive") if x > 0

?

Todos os pontos positivos.

Eu realmente gosto da sintaxe que @StefanKarpinski propôs pela primeira vez em seu primeiro comentário. Estou principalmente interessado em ter geradores condicionais, com uma forma curta mais geral _if_-form como bônus.

Em relação aos geradores, eu estava lendo o if (ou where - não tenho opinião sobre isso) para fazer parte do intervalo... está criando um iterador x = 1:10 if/where x % 2 == 0 , que é então combinado com a expressão à esquerda para criar um Array (ou gerador).

De certa forma, x = 1:10 where x % 2 == 0 é um gerador para algum tipo de iterável. Esta poderia ser uma sintaxe autônoma, não?

Eu sinto que a filtragem é de alguma forma uma operação diferente da instrução condicional if a then b . A filtragem atua no intervalo de iteração. Quando o if a then b é combinado com a expressão à esquerda do gerador, eu esperaria que ele emitisse nothing nos casos em que a era false . Compare o que acontece logicamente se eu adicionar colchetes:

[(x^2 if i % 2 == 1) for i = 1:10] # [nothing, 4, nothing, 16, nothing, 36, nothing, 64, nothing, 100]
[x^2 (for i = 1:10 where i % 2 == 0)] # [4, 16, 36, 64, 100]

Tomando os exemplos de @ararslan , se permitirmos

println("positive") if x > 0

então IMHO segue que

x^2 if x % 3 == 0 for x = 1:100

provavelmente deve emitir nothing, nothing, 9, nothing, nothing, 36, ...

Finalmente, se adotarmos uma condicional abreviada if , meu voto é para a condição antes da declaração, pelas razões descritas no primeiro post de @ararslan - sendo o principal de menor surpresa na leitura do código. (lembre-se, se eles dizem que o código é lido mais do que escrito, então if a then b é a melhor sintaxe que a && b mesmo que o último seja mais curto). Isso também significa que os dois tipos possíveis de if dentro do gerador seriam distintos - na extrema esquerda - com a expressão - ou na extrema direita - com o intervalo.

Quanto à tradição em Ruby/Perl, acho melhor tentar encontrar a melhor solução em vez de ficar preso à tradição. Se funcionar e parecer natural, as pessoas vão gostar.

Além disso, se tivermos if/where em intervalos, precisamos ter cuidado para garantir que o intervalo permaneça cartesiano para geradores multidimensionais?

# Make a circular array (filled with distance to center)
r = 5
[sqrt(x^2 + y^2) for x = -5:5, y = -5:5 where x^2 + y^2 <= r^2]

Isso é meio legal e meio horrível!

Eu sinto que a filtragem é de alguma forma uma operação diferente da instrução condicional if a then b.

Sim! Ia postar esse mesmo comentário. A filtragem opera no iterador como um todo e não faz parte da expressão computada dentro dele.

Eu também sinto o mesmo que @andyferris que o if entre a expressão e a iteração parece "transformar" o valor da expressão em vez da forma da iteração e deve produzir nogthing quando a condição não é atendida (mas posso ser estragado por compreensões python/haskell).

Obviamente x if false seria avaliado como nothing quando não estivesse no contexto de uma expressão geradora. Seria perfeitamente razoável suportar [x^2 if x % 3 == 0 for x=1:100] mas não x if y por si só.

Pessoalmente, prefiro poder omitir end se houver apenas uma declaração.

if length(A) != length(B) throw(ArgumentError("..."))

Se não houver end , ele poderá assumir que havia apenas uma expressão. Eu estou supondo que foi discutido longamente antes?
Observo que preferiria não ter um ponto e vírgula após a condição - após a expressão... _talvez_... Mas não gostaria disso.

Eu prefiro isso, do que ter duas sintaxes diferentes para uma forma semelhante de instrução if;if x; ...; end e if x then; ... . _(Edit: if condition then action cresceu em mim.)_

Seria perfeitamente razoável suportar [x^2 if x % 3 == 0 for x=1:100] mas não x se y por si só.

@StefanKarpinski Estou totalmente de acordo com isso. Contudo...

Se a cláusula if estiver à esquerda, os dados "fluirão" da extrema direita para a extrema esquerda até o meio.

Eu leio x^2 if x % 2 == 0 for x in 1:10 como x^2 where x % 2 == 0 for each x in 1:10 , o que, por ser de compreensão, faz sentido para mim; você está mais interessado na transformação.
Em consultas SQL, o mesmo ponto é verdadeiro, não?

Portanto, não acho que ter o "gerador" como ponto de partida para lê-lo seja muito correto, _para uma compreensão_. Um loop for, por outro lado... Isso faz mais sentido.

Pessoalmente, prefiro poder omitir end se houver apenas uma instrução.

Eu propus isso há muito tempo, mas não obteve tração: https://github.com/JuliaLang/julia/issues/1657. Na verdade, essa seria uma mudança relativamente não disruptiva, pois seria estranho e raro ver algum código de escrita como este:

if cond body
end

O que você acha de usar blocos do em vez de then ? ou seja: if x do y end

Apenas um pequeno comentário lateral: não vejo como if x y por si só (ou seja, fora de um gerador) possa ser tratado corretamente em um editor sem um analisador completo de julia, já que você precisa ser capaz de determinar quantos tokens seguem o if . Estou pensando no vim, mas suponho que outros editores possam estar na mesma situação. O mesmo pode ser verdade sobre y if x , mas não tenho certeza (divulgação completa: também não gosto y if x por si só; não sou contra if x y em princípio, mas acho que pode ser um pouco difícil de analisar também para humanos, não apenas para editores).

@diegozea Mesmo problema, eu acho. Eu ainda prefiro if x y .

@carlobaldassi Existem algumas coisas que você pode fazer para facilitar a análise dos humanos.

Você poderia forçar essa instrução única ifs não teria uma nova linha entre a condição e a expressão.
Não tenho certeza se gostaria de ser forçado a fazer isso por longas expressões, no entanto.

Alternativamente, você pode forçar uma nova linha após a expressão se houver uma nova linha após a condição; portanto, o seguinte não compilaria, mas forneceria uma mensagem de erro clara, é claro:

if length(A) != length(B)
    throw(ArgumentError("lengths must match")
if some_condition(A, B)
   N *= 2

Mas, isso seria:

if length(A) != length(B) throw(ArgumentError("lengths must match")
if some_condition(A, B) N *= 2

e isso seria:

if length(A) != length(B)
    throw(ArgumentError("lengths must match")

if some_condition(A, B)
   N *= 2

Isso também evitaria que você cometa esse tipo de erro:

if is_present(x)
    y = 2 * x[]

    return y * 2

Acho que o maior argumento para if x then y que ainda vence qualquer outra sintaxe é a legibilidade . Que if x y end , if x y ou x && y sejam mais curtos é irrelevante no meu livro (além do fato de estarmos falando de uma diferença trivial de 1-5 caracteres) porque nenhum desses são quase tão claros quanto if x then y . Também evita problemas de análise do editor ou possível quebra de código anterior. É apenas uma sintaxe agradável, limpa e curta para instruções if curtas.

No entanto, reconheço a confusão potencial do uso de if-then no caso do gerador; dado isso, concordo que

x^2 if x % 3 == 0 for x = 1:100      # filtered generator
[ x^2 if x % 3 == 0 for x = 1:100 ]  # filtered comprehension

é mais clara, reconhecendo que a estrutura da expressão é

generated_value_expr  value_generator_expr  =>  generator_expression

[generator_filter_expr]  for_generator_expr  => value_generator_expr

ou seja, o generator_filter_expr é aplicado diretamente aos valores gerados de for_generator_expr antes de passar os valores não filtrados para o generated_value_expr .

Acho que a distinção é importante aqui, porque não é essa mesma lógica que permitiria

println("positive") if x > 0 

TL;DR: Devemos ter x^2 if x % 3 == 0 for x = 1:100 para geradores filtrados, mas a mesma lógica de sintaxe não se aplica a println("hey") if x > 0 , portanto, ainda devemos considerar if x then y para forma curta if-sintaxe. Embora, obviamente, as duas ideias aqui não estejam agora completamente relacionadas.

@ H-225 Isso parece estranho e, em última análise, não-Julian para mim, especialmente com a ação em uma nova linha sem end .

Ainda seria um pesadelo para um editor analisar porque os editores (como o Vim) não se importam com o que é e o que não é uma condição ou ação - na verdade, eles não têm como saber sem um analisador de Julia - mas eles se preocupe com o posicionamento de coisas como if e end . Acho que seria excepcionalmente difícil fazer o Vim reconhecer que end não é necessário nesse cenário. Percebo que facilitar para os editores não é um bom argumento para uma decisão de design, só estou dizendo.

Definitivamente :-1: por if x y de mim. Eu preferiria if x then y a isso, pois é mais fácil para o meu pequeno cérebro analisar. :stuck_out_tongue_winking_eye:

Existe uma razão para não usar simplesmente o operador de ponto de interrogação ternário para a instrução if também? Você já pode imitar uma instrução if fazendo

condition ? if_condition_true_eval_expr : nothing

por que não apenas fazer com que, se você não incluir dois pontos, seja uma instrução if, você teria apenas

condition ? if_condition_true_eval_expr

desta forma você não tem que introduzir novas palavras-chave, isso funcionaria?

@esproff Curiosamente, eu ia sugerir o oposto: estender a ideia if-then neste julep para uma única linha de expressão if-then-else

if cond then a else b

onde a cláusula else é opcional. Pode até diminuir de forma idêntica para cond ? b : c . Para mim, trata-se de fugir do ternário do estilo C e em direção a um código mais legível por humanos. (Eu definitivamente usei condition ? a : nothing antes - parece um hack (porque era, o nothing era importante por algum motivo obscuro) e é confuso para outras pessoas lerem).

Mas é claro, por que não podemos ter todas essas ideias simultaneamente?

@andyferris Sim, esse é o outro caminho a seguir, Pythonic. Eu acho que depende de quão conciso você quer ser, os matemáticos geralmente parecem prezar a concisão, mas honestamente eu ficaria feliz com qualquer um, desde que eu não tenha que usar o desajeitado

if condition; eval_expr; end

mas honestamente eu ficaria feliz com qualquer um, contanto que eu não tenha que usar o desajeitado

if condition; eval_expr; end

De fato!

Por que vale a pena, eu gosto da notação x && y e não vejo isso como um abuso.

Eu pessoalmente prefiro uma solução baseada em operador que não retorne false se a primeira declaração for falsa. Eu ficaria bem com praticamente qualquer coisa, embora eu goste de a ?: b como a versão de dois argumentos do operador ternário.

A ideia de ? foi discutida extensivamente em #6823, pode valer a pena reler essa discussão.

Cheguei aqui a partir dessa discussão, mas achei que este era um lugar melhor para comentar. Eu acho que if a then b e usar apenas ? sem : seria um pouco confuso, mas não reclamaria, desde que eles forneçam a mesma funcionalidade.

Concordo com @EricForgy. Talvez eu esteja acostumado com eles neste momento, mas para mim, usar && e || dessa maneira parece idiomático para Julia, principalmente para verificação de erros (por exemplo x || throw(y) ). Eles não parecem tão ambíguos ou ilegíveis. Além disso, eu realmente não vejo por que isso importaria que x && y retorne false se x for falso porque provavelmente não deveria ser usado no final de uma função de qualquer maneira , onde afetaria o valor de retorno da função.

Os ternários de estilo C são bastante onipresentes; Eu diria que não são apenas os matemáticos que valorizam essa sintaxe. A única linguagem em que consigo pensar de improviso que tem ternários, mas não suporta o estilo C, é o Python, e FWIW eu desprezo os ternários do Python. condition ? action1 : action2 é compacto e legível, desde que você use espaços em branco e respeite os comprimentos das linhas. Para condições e ações mais longas, deve-se usar

if condition
    somelongaction1
else
    somelongaction2
end

de qualquer forma para a legibilidade.

Em relação then , o ponto desta discussão, FWIW eu provavelmente não usaria mesmo se fosse implementado porque, novamente, && e || parecem idiomáticos para mim. Embora eu levaria then em um piscar de olhos sobre if x y , x ? y ou x ? y : nothing .

Pessoalmente, prefiro y if x . Pode parecer o caminho errado, mas há precedência além de Ruby e Perl: a sintaxe de caso matemático tem esse caminho

image

Eu não gosto de usar && ou || para isso, pois nunca me lembro que x && y = z não analisa da maneira que eu esperava. Acho que tem uma carga cognitiva maior que y if x ; isso vale if x y também.

IMO, x ? y _smells_ como um erro de sintaxe, independentemente de ser realmente válido.

x ?? y talvez? x ?: y é uma extensão GNU C que significa x ? x : y , então ?? e ?: podem ser versões de baixa precedência de && e || , talvez.

Minha ordem de preferência: y if x == x ?? y < if x then y < x && y < x ? y < if x y .

FWIW. Eu prefiro a concisão e a separação clara do código com ? , && e || , embora a sintaxe cond ? expr possa ser mais clara que && .

Eu gosto de fazer frequentemente o tipo de fluxo goodcondition || return , o que não é fácil com instruções if. Eu não gostaria de perder isso (ou ser intimidado a não usá-lo)

Para ternário sem o : , pode ser bom permitir blocos concisos if / elseif sem uma condição else :

x==5 ? f1() :
x==6 ? f2()

e pode-se imaginar estender isso para instruções do tipo switch:

match x:
    5 ? f1() :
    6 ? f2()

Em termos de legibilidade, depende. Para muitas condições/ações curtas, é muito mais legível (com espaçamento limpo!!) poder alinhar condições/ações em linhas sucessivas com separadores claros.

Para geradores, eu preferiria where ou when em vez de reaproveitar if , pois é um filtro, não um condicional:

x = [i^2 for i in 1:10 when i%2 == 0]

Eu acho que é mais claro que isso significa x = [i^2 for i in filter(..., 1:10)] .

Vou notar que, desde que discuti isso, eu não me oporia mais a if condition then action - isso cresceu em mim.
Julia tende a se inclinar para o lado mais "palavroso", com function e end , etc, então acho que se encaixaria bem. Também é um pouco irritante ter que adicionar colchetes ao redor expressões se você alternar entre a sintaxe condition && action .

Em relação à sintaxe Perl/Ruby "ação se condição": Eu realmente gosto dessa sintaxe e, embora eu concorde que ela pode reduzir a legibilidade quando usada incorretamente, também acredito que às vezes _aumenta_ a legibilidade.

Por exemplo:

throw(DomainError()) if some_condition || some_other_condition && so_on

Depois de ler "jogar erro de domínio se" você já sabe que o que se segue será uma verificação de sanidade na entrada. Você pode pular para a próxima linha se os detalhes da condicional não forem importantes para você.

A frase anterior é um exemplo de como essa construção ocorre frequentemente em linguagem natural. A ação "pular para a próxima linha" vem primeiro, e a menos importante "se você não estiver interessado" vem em uma oração subordinada. Novamente, o leitor pode adivinhar aproximadamente o que se seguirá à palavra "se" sem realmente ler até o final da frase.

(Também darei uma frase de exemplo auto-referencial, se puder pensar em uma.)

Como um veterano em Perl, sou simpático a essa sintaxe, mas acho que será difícil vendê-la para outras pessoas que não têm experiência em Perl. Um argumento a favor é que ele corresponde à sintaxe do filtro nas compreensões. Um argumento contra é que é estranho ter uma construção em que a ordem de avaliação não seja da esquerda para a direita. É claro que as compreensões têm exatamente isso, então 🤷‍♂️

Para mim, uma pergunta relacionada é: estamos generalizando a ramificação ou a filtragem? Esta é uma generalização da semântica (e sintaxe) if de ramificação, então talvez seja lamentável emprestar a sintaxe de compreensões / geradores para este propósito onde essa sintaxe já indica filtragem.

(A propósito, eu adoraria uma boa sintaxe para filtragem, como nossa boa sintaxe . . Para ilustrar meu ponto acima, talvez map - filter possa parecer f.(a) if g.(a)

f(x) for x in a # lazy map
f(x) for x in a if g(x) # lazy map - filter
x for x in a if g(x) # lazy filter where `f` is `identity`

f.(a) # map / broadcast
f.(a) if g.(a) # like a map/broadcast - filter operation
a if g.(a) # like the above where `f` is implicitly `identity`
[1,2,3] if [true, false, true] == [1, 3] # or something... here we simply make `if` an infix operator for filtering

Desculpe por sair descontroladamente do tópico e não tenho certeza se o acima é uma boa ideia, mas apenas apontando que pode haver outro uso potencial da sintaxe de filtragem de compreensão if que tem semântica de filtragem)

Finalmente - eu meio que concordo com as observações de @tbreloff acima ... binário ? é a sintaxe mais compacta (e todo o objetivo deste tópico é fazer uma sintaxe compacta), e sempre encontrei if uma escolha um tanto surpreendente para filtragem de gerador.

@StefanKarpinski escreveu:

A sintaxe do modificador if/for no estilo Perl/Ruby parece que combinaria melhor com isso. Em outras palavras:
julia println("positive") if x > 0 # conditional execution x^2 for x=1:100 # generator [ x^2 for x=1:100 ] # comprehension x^2 if x % 3 == 0 for x = 1:100 # filtered generator [ x^2 if x % 3 == 0 for x = 1:100 ] # filtered comprehension
Ele também tem o benefício de ter precedentes em outras linguagens, que a sintaxe if-then without end parece não ter.

E seria melhor para a gramática também.

Acho que [x^2 if x % 3 == 0 for x = 1:100] deveria ser:

[(x^2 if x % 3 == 0) for x = 1:100]
 ```
Then `for` stay's in infix position which is currently an error. Of course we can change its meaning because of leading `[` but it would not work as generator:
```julia
x^2 if x % 3 == 0 for x = 1:100

Acho que [x^2 if x % 3 == 0 for x = 1:100] deveria ser:

Isso não seria um gerador filtrado. 2 if x retorna 2 quando x é verdadeiro, mas também tem que retornar algo quando x é falso; normalmente isso seria nothing . Então isso daria uma matriz de números e nadas. É em parte por isso que if tem que vir depois for na sintaxe do gerador filtrado.

Sim você está certo. Talvez devesse ser escrito assim:

[for x = 1:100 x^2 if x % 3 == 0]

Afaics, isso seria analisável válido sem o uso de parênteses, legal!

Só pensando...

[ for x = 1:100 if x % 3 == 0 push x^2]
for x = 1:100 if x % 3 == 0 push x^2  # other keyword could be used, e.g. yield

Isso é mais semelhante à construção natural

for x=1:100 
    if x%3==0 
          push!(somearray, x^2)
    end
end

Acabei de ver este julep novamente enquanto olhava para outra coisa.

Eu ainda desejo algo como o formulário de linha única if a then b ; Eu acho que a substituição juliana a && b é bastante ilegível mesmo depois de usá-la por vários anos: simplesmente não há como lê-la diretamente como uma frase em inglês.

Eu também sinto que a necessidade de explicar o idioma a && b para os recém-chegados é meio embaraçoso quando poderíamos ter uma sintaxe alternativa que é auto-evidente e apenas um pouco mais longa.

simplesmente não há como lê-lo diretamente como uma frase em inglês

a && b lê como "a e b" como em "se a então também faça b"
a || b é lido como "a ou b" como em "a deve ser verdade, caso contrário, faça b"

Eu sei o que ele faz e eu o uso de vez em quando por brevidade. Mas por mais que eu tente (como mencionado, há vários anos), não consigo ver "a e b" como uma frase. É apenas uma leve dissonância cognitiva todas as vezes.

Em contraste, sempre achei "a ou b" bastante legível.

Curiosamente, diferentes aspectos disso surgiram duas vezes hoje no trabalho.

De manhã, tivemos uma Julia PR que usou && em vez de if ... end e eu ressaltei que, como leitor de código (revisor de PR), exigia um esforço extra (e era fácil perder ) ramos que podem ou não ser executados. O exemplo foi da forma a() && b!() onde b! é extremamente mutate-y. (Neste caso, b!() moveu ou excluiu arquivos no sistema de arquivos, o que parecia imediatamente perigoso, mas meu cérebro não conseguia entender que isso só era problemático quando !a() e que este caso foi realmente protegido corretamente).

À tarde, outro engenheiro de software (que não conhece Julia) apontou que, ao falar inglês com amigos, às vezes respondia a pergunta do formulário "É a ou b " com a resposta "sim". Apenas seus amigos engenheiros de software entenderiam – todo mundo não entenderia nada . Pessoas normais simplesmente não pensam assim. 🙂 Isso se relaciona um pouco com o meu próximo ponto (EDIT: e eu deveria ter dito, se relaciona com a resposta de Stefan acima, que para muitas pessoas eu não acho que ocorreria a eles).

Minha posição sobre essa questão o tempo todo é que usar && (ou || ) é uma sintaxe curta para ramificação que pode ser lida apenas por pessoas com um forte histórico de PL e, mesmo assim, adiciona um pouco de carga cognitiva/visual (simplesmente para distinguir & vs && ). Eu sinto que trabalhar em equipes multidisciplinares (mistura de cientistas e engenheiros de software) é ativamente confuso para metade da equipe. Mesmo como engenheiro de software, sinto que temos uma estranha desconexão entre o &(::Bool, ::Bool) --> Bool lógico e o &&(::Bool, ::Any) --> Any ramificado (sim, o último não é realmente uma função, mas espero que você entenda) . Além dos próprios tipos, em Julia eu normalmente espero que o primeiro seja "funcional", enquanto a última forma geralmente envolve potenciais efeitos colaterais - especialmente na segunda expressão.

EDIT: pensando mais nisso, o problema aqui é inteiramente sobre os efeitos colaterais e o fluxo do programa. É muito fácil para todos entenderem que o uso "funcional" de && é uma otimização de & . É relativamente difícil conciliar que eles são totalmente diferentes para expressões não puras.

Em alguns casos, acho que "precedência" é mais clara com if a then b :

guard && c += 1    # probably an error because it's parsed as (guard && c) += 1
guard && (c += 1)  # parentheses required 

if guard then c += 1  # no ambiguity here

Também o realce de sintaxe nos editores ajudaria a marcar if no início da expressão.

Ele também tem o benefício de ter precedentes em outras linguagens, que a sintaxe if - then sem end parece não ter.

Deve-se notar que _há muitos_ precedentes para a sintaxe if-then-else :

E essa sintaxe pode ser usada para one-liners em todas as linguagens acima.

Estou ciente dessa discussão há uma hora, então me perdoe se isso foi sugerido.

Parece-me que as opções de curto-circuito de '&&' e '||' são usados ​​porque queremos instruções if simples em uma linha, sem ponto e vírgula. Mas o curto-circuito parece uma correção que cria outro problema: como humano, é difícil compreender e analisar essa sintaxe sem tê-la visto antes, ou sabendo que a segunda expressão só é avaliada quando necessário. A leitura não intuitiva (como humano) parece ser devido a imperfeições visuais e lógicas com curto-circuito. Parece até que depois de saber o que significa, pode ser mais difícil do que deveria ser difícil de ler.

Se não me engano, ambos os problemas podem ser corrigidos com uma macro:

resultado da condição @if

A negação pode ser tratada com um ! antes da condição ou, alternativamente, uma macro @ifnot . Sou apenas eu, ou isso é livre de ambiguidade para o computador, fácil de ler para o humano, e tudo em uma linha?

Parece até que depois de saber o que significa, pode ser mais difícil do que deveria ser difícil de ler.

^ Concordo plenamente com isso.

resultado da condição @if

Você já pode fazer o seguinte, que parece quase o mesmo:

if condition result end

Abaixo está uma macro para a sintaxe completa do if-then-else. No entanto, como if e else são palavras-chave reservadas, a macro usa If , Then e Else .

syntax_error() = error("Valid syntax is either `<strong i="20">@If</strong> cond Then ex` or `<strong i="21">@If</strong> cond Then ex1 Else ex2`")

function If(exprs...)
    n_args = length(exprs)

    if n_args == 3
        if exprs[2] != :Then
            syntax_error()
        end

        ex = quote
            if $(exprs[1])
                $(exprs[3])
            end
        end
    elseif n_args == 5
        if ( exprs[2] != :Then ) || ( exprs[4] != :Else )
            syntax_error()
        end

        ex = quote
            if $(exprs[1])
                $(exprs[3])
            else
                $(exprs[5])
            end
        end
    else
        syntax_error()
    end

    return esc(ex)
end

macro If(exprs...)
    If(exprs...)
end

foo(x) = <strong i="22">@If</strong> x > 0 Then println("greater than zero") Else println("less than zero")

E aqui podemos ver foo em ação:

julia> foo(3)
greater than zero

julia> foo(-3)
less than zero

Eu preferiria algo como

x0 = 1
x1 = 2
x3 = 3 when y>0  # y>0 is evaluated first
x4 = 4

quando teria menos precedência que = e operava da direita para a esquerda.

Você já pode fazer o seguinte, que parece quase o mesmo:

if condition result end

Eu não sabia disso! Eu sempre pensei que precisava dos pontos e vírgulas onde normalmente há uma nova linha. Então, no meu caso, a sintaxe

if condition result end

torna completamente redundante o uso de && , então não há problema! Vejo que se pode até fazer o seguinte:

if condition result_1 else result_2 end

Nesse caso, adicionar a palavra-chave end provavelmente é mais fácil do que criar uma macro. Mas obrigado por dedicar um tempo para fazê-lo de qualquer maneira ^_^ É uma boa ideia adicionar a possibilidade de instruções if de uma linha na documentação? Vejo que se alguém fizer ?If<enter> o resultado já são algumas linhas... Mas acho que esse recurso deveria ser mais divulgado.

Em ressalva à discussão original, sou a favor de adicionar "Se x, então y", embora eu ache um pouco redundante com a versão de uma linha de "se". Mas ei, escreva o código da maneira que fizer sentido para você, certo?

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

dpsanders picture dpsanders  ·  3Comentários

omus picture omus  ·  3Comentários

iamed2 picture iamed2  ·  3Comentários

yurivish picture yurivish  ·  3Comentários

omus picture omus  ·  3Comentários