简体   繁体   中英

Ruby: why this WHEN-statement doesn't work with two conditions?

I have a case in my Ruby code that basically tries to match string in a couple of ways and do different things to it accordingly, kind of like that:

case inst
when Rx_inst1
  (some function)
when Rx_inst2
  (some other function)
end

This works beautifully (I'm not posting the RegEx as it's very, very long and works well). However, when I tried implementing the following modification, so that even when the RegEx matches a string, the condition is not executed if it contains a substring:

case inst
when Rx_inst1
  (some function)
when Rx_inst2 && !/dom\./
  (some other function)
end

it Rx_inst2 stopped matching inst when it was equal Nm. Friese = Fryzyjczyk, koń fryzyjski Nm. Friese = Fryzyjczyk, koń fryzyjski or any other kind of string it matched before.

I got the exact same result with && /dom\\./ and && inst.include?('dom.') which, according to other answers, are proper syntax. What am I doing wrong?

case inst
when Rx_inst1
  (some function)
when Rx_inst2 && !/dom\./
  (some other function)
end

case translates into bunch of if/elsif/else statements with .=== operator so your code is equvalent to

if Rx_inst1 === inst
  (some function)
elsif (Rx_inst2 && !/dom\./) === inst
  (some other function)
end

and (Rx_inst2 && !/dom\\./) === inst works like this:

  1. (Rx_inst2 && !/dom\\./) part evaluates to !/dom\\./ (Rx_inst2 is a RegEx and is not nil or false)
  2. !/dom\\./ === inst gives you the result

so when Rx_inst2 && !/dom\\./ matches whenever !/dom\\./ === inst matches

It's pretty obvious, Rx_inst2 && !/dom\\./ returns (due to short circuit evaluation) value of the second operand, which is !/dom\\/ (provided Rx_inst2 is indeed a Regexp and thus truthy). You probably need to modify this regexp in Rx_inst2 , there's no short answer.

Alternatively, you could change all this case expression a little bit, like this:

case
when inst =~ Rx_inst1
  # (some function)
when inst =~ Rx_inst2 && !(inst =~ /dom\./)
  # (some other function)
end

but it isn't expecially elegant form, in my opinion.

@Darigazz has nicely explained how case works and why your code is faulty. Here are a couple ways you might write your case statement.

#1

case inst
when Rx_inst1
  m1  
when Rx_inst2
  m2 if inst.match? /dom\./
end

nil is returned if inst.match? /dom\\./ #=> false inst.match? /dom\\./ #=> false in m2 if inst.match? /dom\\./ m2 if inst.match? /dom\\./ . Depending on requirements, if might be replaced by unless .

#2

case inst
when Rx_inst1
  m1  
when /(?=.*dom\.)#{Rx_inst2}/
  m2
end

where (?=.*dom\\.) is a positive lookahead . For example,

Rx_inst2 = /Bubba|Trixie/
/(?=.*dom\.)#{Rx_inst2}/
   #=> /(?=.*dom\.)(?-mix:Bubba|Trixie)/

Depending on requirements, (?=.*dom\\.) might be replaced by the negative lookahead (?!.*dom\\.) .

Okay, so it turned out I made it work like that:

when (!inst.include?('dom.') and Rx_inst2)

I still don't understand why I had to do all these three following things, though:

  1. change the order of operations
  2. bracket the entire expression
  3. call first one explicitly, and the second one implicitly

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM