Live data from Hacker News

Pythonic monotonic

nedbatchelder.com

11–20 of 64 posts

Re: Pythonic monotonic

#11
Choosing either implementation as "more Pythonic" than the other feels to me like choosing tabs over spaces, or vice versa:

https://www.youtube.com/watch?v=V7PLxL8jIl8

i.e., it's a matter of personal preference -- except that, as gizmo686 points out below, as of Python 3 tabs are now, officially, considered more Pythonic.

Re: Pythonic monotonic

#12

The first case is clearer to me than the second, by quite away. Iter, next? Opaque testing of [-1] values? Adjusting a few identifier names, the first actually describes what's going on. Eg., rename `Monotonic()` to `CurrentLtLast()`

The problem is simple enough to solve without function calls at all (except maybe a comparator function) and this is why I like the second solution better: it is closer in spirit to the level of abstraction of the problem.

I think OP missed the point here: the interviewer was probably testing the candidate's Python chops by offering an opportunity to show off their knowledge of language features and standard library on a simple problem. The result is contrived by using __call__ and itertools, but is better for the purposes of the interview.

Re: Pythonic monotonic

#13
post #2

I like how the first one uses groupby() but I don't like the definition of "Monotonic" inside of the module, nor the name, nor returning a list instead of a generator. I also prefer functions instead of callable instances. One alternative to use a function closure instead of a class: import math import itertools def compare_with_previous(): prev = -math.inf def compare(value): nonlocal prev test = prev Another is to…

Why is [2, 1, 1] not being reversed? Since you do call reverse(), I'm not even sure what the bug is on first inspection, which is not a good sign!

Edit: I believe the bug is that the code assumes the first sequence is increasing. It seems a bug carried by all these variants that starts by declaring a -inf variable at the top.

IMO, the problem is trying to find a solution in one's head, then trying to write Pythonic code. The issue is that when someone else reads the code, one does not have the unwritten idea you had in your head.

To write simple code, you must have the least amount of idea in your head. I usually try to write the code dumbly, even if it is inefficient, then do a second pass to remove the most offending parts and maybe make the code more general.

Re: Pythonic monotonic

#14
post #11

Choosing either implementation as "more Pythonic" than the other feels to me like choosing tabs over spaces, or vice versa: https://www.youtube.com/watch?v=V7PLxL8jIl8 i.e., it's a matter of personal preference -- except that, as gizmo686 points out below, as of Python 3 tabs are now, officially, considered more Pythonic.

Spaces are more pythonic than tabs.

https://www.python.org/dev/peps/pep-0008/#tabs-or-spaces

Re: Pythonic monotonic

#15

I’m not a python dev. Can anyone brrakdown the first code? I’m a bit confused

I am a Python dev of too many years and I neither could nor would want to. It's one of those penis measuring contest as interview question questions. I like the concept of "furrowed-brow code".

Re: Pythonic monotonic

#16

I’m not a python dev. Can anyone brrakdown the first code? I’m a bit confused

itertools.groupby() groups subsequences by equality of some common value.

By default, this is the object itself:

  >>> import itertools
  >>> for key, subseq in itertools.groupby("AAAbbCcc"):
  ...   print(key, "->", list(subseq))
  ...
  A -> ['A', 'A', 'A']
  b -> ['b', 'b']
  C -> ['C']
  c -> ['c', 'c']
You can specify how to get an alternative key, like folding everything to lowercase (assuming that makes sense for the target language):

  >>> for key, subseq in itertools.groupby("AAAbbCcc", key=str.lower):
  ...   print(key, "->", list(subseq))
  ...
  a -> ['A', 'A', 'A']
  b -> ['b', 'b']
  c -> ['C', 'c', 'c']
The "key" here is a function which is applied to each subsequent element. You can see that "C" and "c" are now treated equivalently. This is because str.lower("C") == str.lower("c").

The first code create a new callable object, call "Monotonic", which keeps track of the previous value, and uses that for the comparison.

Suppose you knew the result of comparing x[i] Then you could use that magic function to group by successive True or successive False values:

  for is_ascending, subseq in itertools.groupby("AAAbbCcc", key=monotonic_key):
    ...
What a Monotonic instance does is keep track of the previous element, so it the next time it's called it can compute the "The Monotonic class uses Python's special __call__ method so an instance can be called as if it were a function. This allows the function-like object to track state over time.

Here's what it looks like when called with successive elements of the example sequence:

  >>> m = Monotonic()
  >>> [m(i) for i in [1, 2, 3, 2, 1, 4, 5, 6, 7]]
  [False, False, False, True, True, False, False, False, False]
When used as the magic key function to groupby, the [1, 2, 3] (all False), the [2, 1] (all True), and the [4, 5, 6, 7] (all False) group together.

Note: This implementation has to special-case the first sequence element. In this case it assumes the previous element is -math.inf, which is the smallest possible value that a Python float may have.

As it happens, this assumes the first element is always part of a monotonically increasing subsequence. This is an invalid assumption, as it could be part of a monotonically decreasing subsequence, like [3, 2, 1].

  >>> m = Monotonic()
  >>> [m(i) for i in [3, 2, 1]]
  [False, True, True]
A correct implementation should return [True, True, True].

Re: Pythonic monotonic

#17
It's kind of interesting how we're discussing which solution is most pythonic, but nobody has managed to write a solution which actually works. This is partially because the problem is slightly ambiguous, but even then nobody seems to have written a version that works for any of the possible interpretations of the problem.

Most solutions suffer from a combination of any of the following problems:

- Not using strict vs non-strict monotonicity consistently

- Failing to identify the correct direction of a run (either not bothering at all or failing for any run starting with two equal values)

- Assuming the first run is increasing (implicitly by comparing with negative infinity)

There's also nobody who seems to have given thought on how to solve the ambiguity when you allow runs that are not strictly monotonic.

Re: Pythonic monotonic

#18
post #2

I like how the first one uses groupby() but I don't like the definition of "Monotonic" inside of the module, nor the name, nor returning a list instead of a generator. I also prefer functions instead of callable instances. One alternative to use a function closure instead of a class: import math import itertools def compare_with_previous(): prev = -math.inf def compare(value): nonlocal prev test = prev Another is to…

Why is [2, 1, 1] not being reversed? Since you do call reverse(), I'm not even sure what the bug is on first inspection, which is not a good sign! Edit: I believe the bug is that the code assumes the first sequence is increasing. It seems a bug carried by all these variants that starts by declaring a -inf variable at the top. IMO, the problem is trying to find a solution in one's head, then trying to write Pythonic c…

Yes, the -inf approach gives the wrong answer. It's seems clever at first, but induces an invalid mental assumption.

Re: Pythonic monotonic

#19

It's kind of interesting how we're discussing which solution is most pythonic, but nobody has managed to write a solution which actually works. This is partially because the problem is slightly ambiguous, but even then nobody seems to have written a version that works for any of the possible interpretations of the problem. Most solutions suffer from a combination of any of the following problems: - Not using strict v…

This is a really good point. I am very big on making code Pythonic, but I always tell junior developers to get the thing to work first. Once it works, we can re-write it with the advantage of hindsight. The hard part is getting people to tell the manager it's going to be a few days longer even though it's already working.
Post reply on HN