Posso usar o operador de intervalo com a instrução if no Swift?


196

É possível usar o operador de faixa ...e ..<com a instrução if. Maye algo como isto:

let statusCode = 204
if statusCode in 200 ..< 299 {
  NSLog("Success")
}

Respostas:


425

Você pode usar o operador "correspondência de padrão" ~=:

if 200 ... 299 ~= statusCode {
    print("success")
}

Ou uma instrução switch com um padrão de expressão (que usa o operador de correspondência de padrões internamente):

switch statusCode {
case 200 ... 299:
    print("success")
default:
    print("failure")
}

Observe que ..<denota um intervalo que omite o valor superior, então você provavelmente deseja 200 ... 299ou 200 ..< 300.

Informações adicionais: Quando o código acima é compilado no Xcode 6.3 com as otimizações ativadas, então para o teste

if 200 ... 299 ~= statusCode

na verdade, nenhuma chamada de função é gerada, apenas três instruções de montagem:

addq    $-200, %rdi
cmpq    $99, %rdi
ja  LBB0_1

este é exatamente o mesmo código de montagem que é gerado para

if statusCode >= 200 && statusCode <= 299

Você pode verificar se, com

xcrun -sdk macosx swiftc -O -emit-assembly main.swift

A partir do Swift 2, isso pode ser escrito como

if case 200 ... 299 = statusCode {
    print("success")
}

usando a recém-introduzida correspondência de padrões para instruções if. Veja também Swift 2 - Correspondência de padrões em "se" .


1
Legal, isso é O (1)? Além disso, seria bom se Swift tivesse uma mão curta para instruções de troca, como Scala, por exemplo. Mas, como você sempre é obrigado a lidar com todas as possibilidades em tempo de compilação no Swift, isso pode não ser realmente viável.
Sky

2
@ Sky: A partir do código de montagem gerado, é possível ver que uma função de biblioteca func ~= (Range<A>, A) -> Boolé chamada. Eu assumiria que esta função funciona com O (1).
Martin R

4
@Downvoter: Algum comentário explicando seria bom, para que eu possa melhorar ou corrigir a resposta ...
Martin R

1
@ MartinR como você conhece qual função chamada pela linguagem assembly.Hopper? 1 para resposta legal
codester

3
@ codester: Eu compilei o código na linha de comando xcrun -sdk macosx swift -emit-assembly main.swifte inspecionei o código de montagem. Então eu costumava xcrun swift-demangle ...desmembrar o nome da função chamada. - Infelizmente, o Xcode ainda não pode criar código de montagem para arquivos Swift, talvez funcione em uma versão posterior.
Martin R

95

Esta versão parece ser mais legível do que a correspondência de padrões:

if (200 ... 299).contains(statusCode) {
    print("Success")
}

2
Exatamente o que eu estava procurando #
Nazim Kerimbekov 22/02

Eu recebo este erro => Não é possível formar Intervalo com upperBound <lowerBound
Alfi

9

Este é um tópico antigo, mas parece-me que estamos pensando demais nisso. Parece-me que a melhor resposta é apenas

if statusCode >= 200 && statusCode <= 299

Não há

if 200 > statusCode > 299

que eu conheço e as outras soluções sugeridas estão fazendo chamadas de função, que são mais difíceis de ler e podem ser mais lentas de executar. O método de correspondência de padrões é um truque útil, mas parece um ajuste inadequado para esse problema.

Editar:

Pessoalmente, acho o operador de correspondência de padrões hediondo e desejo que o compilador suporte a if x in 1...100sintaxe. Isso é muuuito mais intuitivo e fácil de ler do queif 1...100 ~= x


1
Você está certo de que esta versão é melhor de ler, apenas tentei responder à pergunta explícita "É possível usar o operador de intervalo ...?" - Mas Xcode 6.3 beta (em modo optimizado) gera exactamente três instruções de montagem para if 200 ... 299 ~= statusCode, nenhuma chamada de função :)
Martin R

13
Na verdade, if 200 ... 299 ~= statusCodedá o mesmo código de montagem queif statusCode >= 200 && statusCode <= 299
Martin R

6
A menos que esse condicional esteja em uma seção crítica que é visitada milhares de vezes por segundo, a preocupação com a sobrecarga de chamada de função é a otimização prematura. Mesmo assim, eu me preocuparia mais com o que uma chamada de função está fazendo do que com o custo de chamá-la. Bom trabalho @MartinR por provar que não há custo, apesar de tudo.
Rickster

1
@ Rickster, é verdade. Eu ainda tendem a preferir construções eficientes do que construções ineficientes por uma questão de hábito (assumindo que a legibilidade seja semelhante). Não na medida em que perco muito do meu tempo com isso, mas ainda vale a pena saber quais são os custos de diferentes abordagens.
Duncan C

1
Isso é óbvio, mas eu discordo de sua sugestão de que sua declaração if é mais legível ou compreensível do que a resposta postada por @SerhiiYakovenko. Simplesmente com base no DRY - você nomeia "statusCode" duas vezes. Em uma sessão de depuração de olhos turvos, tarde da noite, depois de ter decidido que uma variável diferente chamada "statusValue" deveria ser usada aqui em vez de "statusCode", eu poderia cometer o erro de alterar um dos nomes de variáveis ​​e não o outro .
RenniePet

3

Eu queria verificar os erros 4xx, exceto 401. Aqui está o código:

let i = 401

if 400..<500 ~= i, i != 401 {
    print("yes")
} else {
    print("NO")
}

2

Também preferi o operador Range .contains (), até descobrir que sua implementação é ineficiente - https://oleb.net/blog/2015/09/swift-ranges-and-intervals/

Podemos representar a condição x <0 usando um intervalo: (Int.min .. <0) .contains (x) é exatamente equivalente. É muito mais lento, no entanto. A implementação padrão de contains (_ :) percorre toda a coleção, e executar um loop nove quintilhões de vezes no pior caso não é barato.

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.