2

I'm trying to translate some code from Python to Ruby but i'm stuck on this one function in python:

def notNoneOf(a, b):
  return a is None and b or a

My naive translation into ruby is like this:

def notNoneOf(a, b):
  return a.nil? and b || a
end

But this is giving me a void value expression

The usage of the function in the python code is as follows:

for m in re.finditer('<input.*?name=(?:"([^"]*)"|([^" >]*)) value=(?:"([^"]*)"|([^" >]*))[^>]*>', data):
    formData[notNoneOf(m.group(1), m.group(2))] = notNoneOf(m.group(3), m.group(4))

From playing with the code in a python REPL, it seems like this ruby code should work:

def notNoneOf(a, b):
  return a || b
end

But that seems like I'm missing some case for this?

This test in python makes it look like it's a bit different:

>>> a = None
>>> b = None
>>> notNoneOf(a,b)
>>> 

Any help appreciated.

Ramy
  • 20,541
  • 41
  • 103
  • 153
  • 3
    That function is not very pythonic *and* unclear as to what it should do. I'd use `a if a is not None else b`, precisely because there appears to be a bug in it if `b` is false-y. – Martijn Pieters May 29 '17 at 13:52
  • @MartijnPieters But your version does not return a boolean any more generally speaking. – Ma0 May 29 '17 at 13:55
  • Next, the Python code appears to be using a regex to parse HTML; use a proper HTML parser instead. – Martijn Pieters May 29 '17 at 13:55
  • @Ev.Kounis: the function doesn't return a boolean. `and` and `or` do not produce a boolean, they produce one of the two expressions. – Martijn Pieters May 29 '17 at 13:55
  • I appreciate your input, @MartijnPieters, but keep in mind i'm not keeping the python code. I'm trying to translate to ruby. – Ramy May 29 '17 at 13:56
  • 1
    [**Do not use regex to parse HTML**](https://stackoverflow.com/a/1732454/1954610). Rather than blindly "translating" that horrendous python code, I'd strongly suggest that you re-write it. – Tom Lord May 29 '17 at 13:57
  • baby steps :-) I need to understand if my translation of the notNoneOf function will work first. – Ramy May 29 '17 at 14:01
  • 1
    I don't have a clue what `return a is None and b or a` even does, just by looking at it... (see: "horrendous python code"). If you list out what that function returns, given the possible input values of `a` and `b`, then it should be easy to get a (much simpler) translation in ruby. Or python. – Tom Lord May 29 '17 at 14:03
  • 1
    Naively, a function called `notNoneOf` (haha...) could be written in ruby as: `def either(a, b); a || b; end` – Tom Lord May 29 '17 at 14:04
  • lol I actually read the link you shared @TomLord - AMAZING – Ramy May 29 '17 at 14:05
  • 1
    ... And I *think*, at a guess, that the weird python function is equivalent to `(!a && b) || a` -- which is just a pointless/longwinded way of writing `a || b`. – Tom Lord May 29 '17 at 14:06
  • 1
    By the way, just for completeness' sake: `and` and `or` are intended to be used for control flow, which is why they have low precedence, which means that your code is parsed as `(return a.nil?) and (b || a)` which means that `b || a` will never run because the method has already returned. Such code is called a "void value" in Ruby jargon. – Jörg W Mittag May 29 '17 at 23:25

1 Answers1

2

Looking at python operator precedence, it appears that the python code:

a is None and b or a

is logically equivalent* to:

(!a && b) || a

* See comments below -- it's not quite equivalent, but the explanation still holds true.

However, this a pointless over-complication. This is, again, equivalent to the much simpler:

a || b

A slight giveaway is the bizarre function name of notNoneOf. A more sensible function name would be eitherOf - which makes the above implementation look immediately correct.

So in ruby, I would just write this as:

def either(a, b)
  a || b
end

(Or, more likely, not even bother abstracting this into a separate method at all!!)


Seeing the wood through the trees however, I would not continue "translating" this code between the two languages ad verbium. The python code looks extremely confusing and bug prone; it's using a regular expression to parse HTML - which is just asking for trouble!

A much better solution is to write this with a HTML parser such as Nokogiri. You could write code something like:

doc = Nokogiri::HTML(data)
doc.css('input').each do |input|
  puts input.name
  puts input.value
end

Figure out exactly what the python code does, and try to replicate its behaviour rather than its implementation.

Tom Lord
  • 27,404
  • 4
  • 50
  • 77
  • ...And I *think* you could replicate the python behaviour with `input.name = input.value` -- but it's worth checking! – Tom Lord May 29 '17 at 14:32
  • Note that the code is only equivalent to `a || b` if `b` is either `True` or `None`. `notNoneOf(False, True)` is `False` for example, because `False is None` is `False` in Python, just like `false.nil?` is `false` in Ruby. Anyway. Your alternative solution is much better. – Eric Duminil May 29 '17 at 17:13
  • Thanks very much for this answer. Sometimes when I come across bad code like this, I have a hard time telling if it's the code that's stupid or *me* that's stupid! Thanks for the confidence that it's not me :-) – Ramy May 29 '17 at 17:35
  • 1
    @EricDuminil Yeah, I wasn't 100% sure about the fine details there - python isn't one of my primary languages... After a little more research however, it appears [*if a group is contained in a part of the pattern that did not match, the corresponding result is `None`*](https://docs.python.org/2/library/re.html#re.MatchObject.group) -- so in the context of that code, `a` will never be `False` and so the logic is indeed equivalent. – Tom Lord May 29 '17 at 21:58
  • @TomLord: Shoot, I forgot to mention this. You're totally right, `False` cannot happen here. Stil, you might want to add a note to `is logically equivalent to:` because it's not true in the general case. Finally, I highly recommend studying a bit of Python if you like Ruby. It's refreshing, pretty easy and can be useful. Cheers! – Eric Duminil May 29 '17 at 22:02