Another of Python features is a great REPL: when unsure or confused by an interviewer, I'd just fire python from shell and type in 'x' == 'x' == 'x' to confirm and demonstrate it does the right thing (or write tests).
Obviously, the interviewer should be careful not to sidetrack the candidate much, and let them do the work and attempt to help only if things don't work out.
found = searched_key in list(large_dict)
vs found = searched_key in large_dict
But also compare: searched_key in large_dict.keys() # O(1)
and searched_value in large_dict.values() # O(n)
I've used Python for 20+ years, and while I'd confidently use a == b == c or 1 < a < 10, I didn't know the specifics and wouldn't use it in cases like 10 > a < 8 (or really, any other case chaining supports).
I believe myself to be an expert at Python and I'll explain differences between different loop types (while/for, ranges/iterators/generators, list comprehensions, functools, external C libraries like pandas or numpy), I think I wouldn't be confused only because I am aware I don't know the details and have a quick way to prove it works.
I got the impression that the candidate was repeating the x == y == z pattern because they'd seen it in other people's code and was pretty sure it worked, not because they knew about comparison chaining. At minimum I'd expect a candidate to be able to clarify that (x == y) == z is not the same thing, not just "idk it works whenever I do it". My reaction to that was: at some point, you do need to at least be able to reason about your own code.
In a more generous light, yes, I agree with everything you wrote. The precise details of comparison chaining are out of scope and I'm sure that most Python developers (myself included) don't know or remember them.
I was told later that while I “obviously knew Python quite well, it didn’t give a good signal for my capabilities.”
If I see such a situation I usually ask the interviewer whether they want a concise solution using libraries or they want to see how I would do this if I had to do it from scratch.
Or I just offer both, I show that I can do it, but that I know it would be easier to do with x.
I think this is a great opportunity to show you're a good communicator as well as a problem solver.
I don't think that anyone who main programs in Python would perceive it as some kind of cool/unusual trick and not just normal code.
If you're giving interviews, and evaluating code in a language you don't know, don't be so confident it's wrong. Ask the candidate how it works.
The important part here is that there are a substantial fraction of python developers that don't share your sentiment.
"Confusing" the interviewer is a totally valid strategy! It's the only strategy when python async is involved!
5 < x <= 25
x == y == 10
When looking at `a == b == c` we naturally assume that not only `a == b` and `b == c` but also `a == c` because equality is assumed to be transitive.
The problem is that in Python we can make `==` operator to do literally whatever we want, so in Python `==` doesn't have to be transitive.
You may argue that no reasonable person would define equality in such way that is not transitive.
To which I would reply that approximate comparison of floating point numbers looks perfectly reasonable to me.
EPSILON = 10**-9
class Foo:
def __eq__(self, other):
return other.bar - EPSILON <= self.bar <= other.bar + EPSILON
45 minutes interviews aren't really the place to evaluate this.
(But, the intent on the interviewee's part was definitely clear - I'd definitely let this pass.)
> The candidate this time was programming in python
But the code blocks aren't in python. But in the paragraph afterwards they capitalize "True" which is a python thing. Then afterwards mention they're using javascript as their reference, which uses "true" instead and the code blocks could be javascript.
It feels like the author has more language confusion going on than just this one feature of python.
You need to detect if someone is humble and smart enough to fit in to how you do things as a team, whatever that is.
Not reject someone for not hitting a style guide on a piece of paper tucked behind a hidden secret door. (As I was once!)
[0] https://github.com/satwikkansal/wtfpython?tab=readme-ov-file...
You're probably thinking `1 === 1 === 1` which is indeed false
Also, why would you conduct an interview in a language where even if you don't know the syntax (and this is obscure) you could have looked it up or disallowed the interview to be done in Python? I think the due diligence with this issue is more to the interviewer than Python.
The norm in most of my interviews has been that candidates can solve coding problems in whatever language they are most comfortable with. For most languages like Python etc, it would be a mistake to reject a candidate just because they don't have experience with that specific language.
I assume they've already filtered out candidates whose "most comfortable language" isn't the one they're hiring for, or they're going to have a difficult time when they come across the one who wants to use APL or x86 Asm.
The only thing I see that's wrong is that the interviewer thinks something went wrong.
> ...the statement even though logical, is not technically right because if we start from the left cell[0][0] == cell[1][1] would evaluate to True and then True == cell[2][2] would evaluate to False given that the cell contains a char.
Interviewer seemed confident ("is not technically right") which confused the candidate:
> I told the candidate about the same and he agreed but he was confused at the same time
Candidates are already in a high stress situation, you don't want to add to it unless you really know what you are doing (i.e. you are specifically looking for deep language mastery).
Interviewer has correctly realized they've done this and learned from it: kudos to them even if too late for this candidate :)
> The interviewer made no mistake though
So is it wrong to admit that you made a mistake? :)
And no, it's ok to make mistakes, but it is even better if we admit to them and learn from them (and the faster we do that, the better it is).
An outstanding candidate when questioned would mention that in most languages this evalates to that, but in python it evaluates to this, and offer an alternate method chaining and's on the spot in response to the confustion.
If they are a bad interviewer - they would allow their ego to derail the interview.
With my last job, I fumbled an easy question for me that I usually wouldn't, but it was a 10pm interview after I got up at 4am that morning and have almost fallen asleep when putting my kids to sleep at 9pm. I got the job (and then got promoted twice in 18 months), but if interviewer "inadvertently" confused me on top of my mental state, it might have been game over for me (and them, since I turned out to be an "outstanding" candidate).
Part of the problem here is that we treat true/false as "just another value" in programming, and thus the usual operators are used to compare them. In math notation, if you wanted to compare the result of comparison A=B to a Boolean value C, you'd normally write something like (A=B)≡C, using different operators for equality and equivalence.
Interestingly, programming languages haven't always treated Booleans in this way. If you look at ALGOL 60, it starts by cleanly separating expressions into arithmetic and logical ones, and defines completely different sets of operators for each (the reference language also uses = for numeric comparisons and ≡ for Booleans; and boolean inequality is, of course, just XOR).
I think that Rust got this right. It doesn't allow you to add integer to boolean or multiply boolean by float etc, because it is unclear what does it even mean mathematically.
Also, most languages implicitly assume that any value of any type is either "truthy" or "falsy" thus you can do something like `while(1) { do_stuff() }`. Rust doesn't allow this BS, `if` and `while` expect a boolean so you have to provide a boolean.
Perl is probably the most awkward due to context-sensitive casting. e.g. the string `"0"` in a boolean context evaluates as an integer, and the boolean interpretation for `0` is false.
[0] https://play.rust-lang.org/?version=stable&mode=debug&editio...
I cannot see it for !=
"a != b != c" is not equal to "not (a == b == c)" which is a bit strange imo
Maybe I'm simple minded though.
This is a fail for the interviewer, not the interviewee, nor for Python.
Do people generally write concise code on right off the bat when confronted with a new problem? I've always taken the same approach I would with writing: get something that works; refactor for concision. Maybe everyone else is playing 3D chess and I'm still on Chutes and Ladders?
I've seen real code that looks like
if(bool_var == false) {
other_bool_field = true;
} else if(bool_var == true) {
other_bool_field = false;
}
Which I suppose could be called anti-concise.There's a basic level of concision you'll have by default if you actually understand the problem and the tools. You won't go away out of the way to complicate things. Maybe it's still a far cry from optimal, and definitely not code golf, but enough that I'd be worried if I saw even a first draft without it.
The risk is that you may end with such concise code that nobody understands what it does. I went that dark path with perl 20 years ago, form "oh what a cool language for someone who comes from C" to "let's squeeze that in half a line, and either do no understand what it does, or add three lines of comments to explain".
But yes, there is a good middle-groud somewhere
1. Get it working
2. Get it working well
If you're faced with an unfamiliar domain, you don't rock up to it and throw all the super optimised fastest code at it. in fact I would view someone who did as having done the test before (making it worthless).
Syntactical sugar doesn’t matter anymore at that stage
As someone who has been on the interviewer side: this is an indicator of how much the interviewee "thinks first"; the ones who immediately start spewing tons of code are usually those who don't really have a good understanding of what they're trying to do.
Think first, code second.
I code similarly to how I write, which is similar to how I draw etc. I start with very rough ideas and sketches, then I gradually iterate over those, refining ideas and throwing bits away. And eventually I have a working version/first draft/etc
I just can't get things done if I were to try and think of the entire solution first before getting anything down on paper. Maybe that's an ADHD thing, needing to let the idea take form lest it runs away and I get lost in the weeds
It's less "spewing tons of code" though and more starting with an outline, stub functions and comments to break up the steps before filling those out.
if any([a in ["-h", "--help"] for a in args]):
display_help()
Would be one of those. Generally I learned that it can be beneficial to treat things as lists early on, e.g. if you have a program that takes one input file it costs you next to nothing to allow the input to be mutiple files as well.I would have gone with the following, as it requires less mental unpacking:
if set(args) & set(["-h", "--help"]):
display_help()
Also, note that any() works with any iterable, the outer brackets are not needed given the list expression. if any(a in ["-h", "--help"] for a in args):
display_help()
> That still looks like something that a novice might trip over when reading this.
my experience with “juniors” who start with python is that they think in lists and dictionaries. they don’t think in terms of set theory. that’s just the way python is taught.
i would definitely set interview questions where a “decent” solution involves using sets and see who gets it without iterating over lists (ha, setting an interview question on sets… geddit? i’ll see myself out).
> Just describing what it does is already a bit convoluted
yeah, but python only/main “juniors” primarily think in terms of lists, see above.
so, between making sure all juniors can read the code versus doing it succinctly, but juniors all need to learn about sets — practicality wins out quite often.
> I would have gone with the following, as it requires less mental unpacking:
> if set(args) & set(["-h", "--help"]):
> display_help()
if i were reviewing this code i’d ask you to switch it to
if set(args).intersection(set(["-h", "--help"])):
it is more verbose and more brackets, but it is more explicit and obvious what is happening to some junior who has never dealt with sets before (hmm what’s an intersection? should probably google it)& is also used for the bitwise AND operator. plus some other stuff in various frameworks like django. so could lead to some confusion.
> Also, note that any() works with any iterable, the outer brackets are not needed given the list expression.
i generally HATE the overuse of generator expressions in python. everyone writes absolutely everything as an inline generator expression, making it hard to test.
but, rant aside, yes, this is probably the one i would pick during a review (likely after a discussion about how using the intersection method makes things less legible and then this gets suggested somewhere).
people don’t realise not every genexp needs to be a list. easy mistake to make with the “everything is a list or a dict” mental model.
if set(args).intersection(["-h", "--help"]):
Also python has had a special syntax for sets since 2.7 (I think), though I haven't seen it used very often: if {"-h", "--help"}.intersection(args):
foo(x for x in xs)
foo((x for x in xs))
gen = (x for x in xs)
foo(gen)
But these are not the same as: foo([x for x in xs]
lst = [x for x in xs]
foo(lst)
It doesn't matter in this tiny example, but the difference between generators and lists can be very very important when working on very large amounts of data (e.g. processing a big text corpus), or a potentially-infinite stream of data.`any` works on arbitrary iterables, so you can use a generator comprehension instead of a list comprehension:
if any((a in ["-h", "--help"] for a in args)):
display_help()
Which algorithmically is a wash, because the time you save not-materializing the list, you waste in the overhead of iterating through a generator (and it's all +/- nanoseconds in recent versions of Python anyway). However, Python has a special syntax that allows you to omit a layer of parentheses when a generator comprehension is the only argument to a function, leaving us with the very elegant (IMO) syntax: if any(a in ["-h", "--help"] for a in args):
display_help()
This works for any(), all(), str.join(), and others. Yet another demonstration of why I think the iteration protocol is one of the best features in Python (and underappreciated as such).In reality - tasks like these are garbage leet-code samples; candidated who have been grinding examples rapidly regurgitate the best solution from the forums and impress the interviewer, but would fail if asked a basic question on what they produced. Those that solve it reasonably from first principles fail the task.
At least ChatGPT recognized and explained it correctly, so it makes picking up new features easier than it used to be. I'm making a mental note to ask LLMs whenever I encounter unknown constructs.
if (cell[0][0] == cell[1][1] == cell[2][2]) {
return Winner
}
> Well the code is indeed correct . Well done, Python… for finding yet another way to confuse us all.The code is incorrect: if all entries are '-' there is no winner. Even ignoring the braces…
Yes, John, some switches do start sending the packet before finish receiving it. It’s called cut-though switching. Hope you googled it. And yes it was worth losing the job offer over it.
"There are no children here at the 4H club either! Am I so out of touch?
No... it's the children who are wrong."
-Principal Skinner, "The Simpsons"
EDIT to clarify- I'm just being silly, not suggesting anyone is right or wrong here.
5==5==5 # All three the same
(5==5)==5
true==5
false
Or 1==2==false # All three different, in a troublesome way
(1==2)==false
false==false
true
Compare this with
a = b = c = 5
You evaluate the innermost (i.e. the tail) expression first, and work your way out to the head. As a sexpr it is a little more obvious that way: (= a (= b (= c 5)))
An argument could easily be made that python is making up special rules for the equality operator by treating the duplication of the operator as merging the expressions into one.Instead of what you would expect, per the rules of literally every other expression, 5 == 5 == 5:
(== 5 (== 5 5))
It gets rewritten as (== 5 5 5)
Which one is "unexpected" is really a matter of perspective: are you reading with the rules of English or the rules of your programming language as your context?I do concede one caveat to this argument: mathematical operators tend to change operator precedence around to avoid making mathematical mistakes. I am mildly of the opinion that this itself was a mistake. Most calculators don't do it, so it's not like anyone would be* that* thrown off by it.
I guess the argument could be made but it would be wrong. All the comparison operators can be chained in python. a < b < c, for example, or even a == b <= c < d.
That's not true in Python though.
a = b = c = <expression>
is equivalent to: temp = <expression>; a = temp; b = temp; c = temp
I don't know why that order of evaluation was chosen.There's a lot of options for a language to deal with a statement like this. It could be a syntax error, a compile or lint warning, it could work by doing order of operations and comparing the boolean from one compare with the third element, or it could work in the way you described.
I'd prefer languages that I work with to chose earlier options from that list. In most languages this sort of statement is usually a bug, and deserves explicit parenthesis to clarify the order of operations. I really don't want to have to pull out an operator precedence chart in order to read your code, much less have to look up some esoterica about this specific language.
I've worked primarily in Python for the last two years and I'd still have to look at a reference to be certain of what exactly this code does. And would probably rewrite it to the expanded, parenthesized form unless the company linter insisted.
Each rule of a language is a cognitive burden. It can only justify itself if the thing it replaces is a greater burden.
Hard disagree here. I write code for people who can read the language. This includes operator precedence.
Additionally, I think assuming the reader is aware of every. nuance of the language is an extremely bad idea unless you are and will always be the only developer. If you are doing anything weird or tricky or rare, I'd highly recommend linking the documentation or providing an explanation.
Programming languages often have syntax that's acknowledged as a source of confusion and bugs and better avoided. The line is not hard and fast, but I write C++ and there's "C++", and then there's "the C++ subset that you should use" (I probably couldn't write C++ code without tools slapping me for using the wrong parts). Operator precedence is debatable but our tools force the use of parentheses to disambiguate.
People unfamiliar with programming will assume that "if a == b == c" is only true if all three things are the same, and people familiar with Python will know that that is, indeed, what it means.
Python 'won' mostly because non-programmers could look at it, more or less understand what was going on, and feel that this was something they probably could learn.
But then is the mathematical '=' really a binary operator that maps two numerical inputs to a boolean output? It feels then as an abuse of notation if (A=B)=C doesn't allow A=B to change type?
Because I don't really have much use for a symbol that is some times doing a binary mapping from numbers to booleans and some times becomes some sort of ternary operator which maps 3 inputs to one boolean.
This actually gets weirder - in python you can make an arbitrary long comparison operator - it's called comparison chaining - https://www.programiz.com/online-compiler/6uyqb52IVH8if . It works with a lot of operators - https://www.geeksforgeeks.org/chaining-comparison-operators-...
Once you know how it works and are used to it, I think it makes the code easier to parse... but there are heavy downsides. For example, it's not clear how short circuiting works. I've used python a bunch and logically I expect ```side_effect3()``` to not be evaluated if results of 1 and 2 are not equal: ```side_effect1() == side_effect2() == side_effect3()```. However, I do not know that for sure, while in other languages I would be able to reason about this from basic principles.
I think most people would expect expressions like `5 < x < 10` to work as they do in math, without necessarily thinking about it in terms of being an overloaded binary operator. The result in other languages that `5 < 12 < 10` = `true < 10` = `true` is more surprising, just that we've (perhaps by being bitten by it early on) gotten used to it.
x = x + 1 sounds wrong if you are in math mode
x := x + 1 is uncommon in math and you can take it to mean "set x to whatever is currently x, plus one"
But it's also true that you need to be able to accept certain conventions when changing fields.
It's not a hard rule that overrides all other considerations, but I do think Python's choice to follow math is the right decision here.
I'd claim if you teach someone `<` then show them `5 < x < 10` for the first time, they're far more likely to go with the math interpretation than the C one. That is, beyond the fact that someone's familiarity with math reinforces this intepretation, the reason for math using this interpretation in the first place is because it aligns with intuition.
It's also just pretty useful. It's common to want to check if a number is between some bounds, like `0 <= i < len`, or that multiple things are equal. In cases where you really do want the C behavior, I'd say you should already write that as `(x == y) == z` for clarity anyway, regardless of whether the language lacks chaining and thus lets you omit the parentheses.
It is clear if you read the docs.
https://docs.python.org/3/reference/expressions.html#compari...
https://docs.python.org/3/reference/expressions.html#boolean...
I’d expect a raised eyebrow if it was some very basic question about something you’ve claimed to be an expert in but given how many, many bugs over the years stem back to confusion about order of evaluation I would consider it a minor plus if someone said “I think I know but I want be certain”, and a major one if they paired that with mention of defensive coding practices like adding tests covering cases where it’d matter and structuring the code so there are no side effects hidden in the test (e.g. the code as written is fine but if it was f1() == f2() == f3() I’d probably write it as a clearer multi-line if block when there are side effects to those calls to make obvious when I’m intentionally not doing them in every case).
Although I'd also want that benefit of the doubt for myself. I'm quite familiar with how Ruby handles collections, for example, but if I tried to use `slices.Collect` in Go the same way as `Enumerable#collect` in Ruby I'd end up stuck and would need clarification.
Extra points if you are familiar enough with the documentation to find the answer quickly.
I once passed an interview where it was all just Spring documentation questions. I had never built a Spring app in my life, I just "read the docs" five times.
Opinion: Code that depends on side effects like that in a conditional is BAD. It is not an optimization, it's an obfuscation.
Example
(a==b)==c would be different from a==b==c
(= 2
(+ 1 1)
(sqrt 4))
; => t
(< most-negative-fixnum 0 most-positive-fixnum)
; => t
2 + 3 + 4
you write (+ 2 3 4)
This is nice for associative operations like + or *, very very very slightly confusing for - and /---
You use the same style for comparisons, so instead of
2 == 3 == 4
you write (== 2 3 4)
To support the fist "infix" syntax, you need some magic in the == operator. But the second "prefix" syntax is totally natural and you need no magic in the parser or the operator.It's fine if you DNF it:
(- a b c) => (+ a (- b) (- c))
(/ a b c) => (* a (/ 1 b) (/ 1 c))
The functions behave like:
= all the same
/= all different
< monotonically increasing
> monotonically decreasing
<= monotonically nondecreasing
>= monotonically nonincreasing
Also * is product and + is sum.
Sure, that's true if you literally know nothing about coding. But that is not a very common audience for reading code. You only need to spend about 10 minutes coding to realise that the compiler follows fixed rules rather than making an LLM-like guess as to the meaning. If you get that far then most people (admittedly after a bit more 10 minutes) go on to realise that the only way to know what code does is carefully pick it apart step by step, rather than glance at it and guess yourself.
I love Python dearly, but this rule was a misstep on my opinion.
1 + 1 + 1 has different operator behavior then 1 == 1 == 1. The operations here are not consistent and it’s not clear what happens if I overload the operators.
On the surface level python looks more beautiful. But mathematically speaking python is actually more ugly and more inconsistent with weird rules to make things look a certain way.
The case where all items are the same type is reasonable, but if implicit type conversions are invoked, it gets really complicated. Whatever the types are, Python will probably do something. Just not necessarily what you want.
I could see having this if there was a compile-time constraint that all variables be the same type. That would disallow the bad cases.
Chained comparisons have been in Python since I started using it at 2.1. It looks to me like they've been there since at least 1.4:
https://docs.python.org/release/1.4/tut/node40.html#SECTION0...
Are you sure you are not confusing it with PHP? Python is not statically typed, but it is strongly typed. There is no implicit type conversion unless it's safe to do so (such as between a float and an int).
For example, print(1 > "foo") will raise a TypeError, and print("1" == 1) will print False.
This can lead to some strange operator semantics in mixed-mode expressions. Whether chained expressions left-associate or right-associate matters.
numpy.array operations do a lot of implicit conversions. You can use Python arrays on many operations that expect a numpy.array. Mixing them works. Mostly.
python3
Python 3.10.12 (main, Sep 11 2024, 15:47:36) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy
>>> a1 = numpy.array([1,2,3])
>>> a2 = [1,2,3]
>>> a1 == a2
array([ True, True, True])
>>> a2 == a1
array([ True, True, True])
>>> a1 == a2 == a1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
>>> a2 == a2 == a2
True
>>> a1 == a1 == a1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
False is False is False. If you thought, True, you were right :)
I would recommend to take a look at one of my favourite repos for more[1].
Which makes me wonder whether they came up with this idea themselves or if they borrowed it from D.
In any case, I think that this is the only reasonable solution for new languages. Because if you go with C way it will be surprising for people who expect Python way and vice versa. So, making this syntax illegal is the only way to uphold the principle of least astonishment [1].
[0] https://play.rust-lang.org/?version=stable&mode=debug&editio...
[1] https://en.wikipedia.org/wiki/Principle_of_least_astonishmen...
When I implemented it, I had seen many knock-down drag-out debates about which was the best way to deal with it, and none of them mentioned simply making it illegal.
It was another one of those features that were based on my experience designing aircraft systems.
Another one adopted by pretty much all languages since D is the _ in numeric literals (although C++ used a backtick). It was just something that was obviously right once you saw it. But I didn't originate it, I cribbed it from Ada. I've always had a soft spot for Ada.
"I like this because the logic is straightforward and it helps to judge things like code quality / speed / conciseness etc."
No, it doesn't.
Everyone who comes up with their own pet way of evaluating and interviewing thinks - nay has the unshakable belief - that their way of evaluating developers is 100% spot on. However it's always something they just made up, justify with plain old words and never science.
Interviewing is inherently messy, imprecise and unscientific. The process involves a lot of guesswork and feeling, rather than accurate measurements of any kind. It creates a fictional work environment that is often stressful for candidates, so any signal you may get out if it is also inaccurate.
It's natural for interviewers to have a preference for certain questions, topics or ways of conducting an interview. You're free to disagree, of course, but claiming that your way is superior is silly.
Also, why not do `all(cell[0][0],cell[1][1]),cell[2][2])` if you want to have True when all 3 are True?
Perhaps I'm missing something, I don't exactly consider myself a 1337 coder.
Yeah, that’s C/C++/JavaScript/… syntax, it’s not valid Python.
> why not do `all(cell[0][0],cell[1][1]),cell[2][2])` if you want to have True when all 3 are True?
We want to test whether all three are the same, not whether they’re all True.
The interviewer also lost his job, since he didn't spot it nor does he know basic python syntax.
WalterBright mentioned that D will give you an error. When designing Starlark (a derivative of Python), I also decided to give an error for this, by making the comparison operators non associative.
https://github.com/bazelbuild/starlark/blob/master/design.md...
I agree this Python feature can look cute, but I've found it's rarely useful and can easily be avoided.
The way it works in Python allows for some hilariously unreadable code, but the concept itself is good. I think it just needs more constraints to maintain readability, such as not allowing flipping the comparison sign (like in x<y>z).
Banning these chained comparison operators is not necessarily a bad idea as comparison operators can easily cause confusion in that manner, but the reason they cause confusion is that many programming languages use mathematical symbols differently than actual math. A=B=C is perfectly understandable and more readable than A=B ∧ B=C. Restraining ourselves to "operand operator operand" as the only infix expression model made sense decades ago when computers had storage counted in kilobits. Today, we can let go of the limitations of yore and use mathematical expressions in a way that plain sense.
0 == '' evaluates to True
0 == '0' evaluates to True
false == 'false' evaluates to False
false == '0' evaluates to True
' \t\r\n ' == 0 evaluates to True
and the === operator you need to overcome the above. I don't have anything against JS and I understand why it is done that way, but if some language has weird and confusing comparison operators is JS.Make == be ~~ or .fuzzyCompare()
And === be ==
And you are fixed.
I don't know many programming languages, but of the few I know any of them has a === because == is so confusing that you don't know exactly if (or when) it would bite you. You can't attack any language for their comparison operators from the Javascript camp.
It that blog post removed the sentence "And this is how it would definitely happen if the code was written in javascript" they would be fine. But you actually don't know what would definitely happen in Javascript with the line in question "cell[0][0] == cell[1][1] == cell[2][2]", unless you know 100% what is in these cells. For example:
cell[0][0] is "0", cell[1][1] is 0, cell[2][2] is true
and "cell[0][0] == cell[1][1] == cell[2][2]" evaluates to True.
If those values came from <input> elements, the second and third examples aren't possible without having manually converted it beforehand. Input values are always strings, even a checkbox. That's where javascript's conversion rules and lax equality came from, an attempt to have sane defaults for simple uses: If a string is being compared to a number, the string is likely from an input and the number was typed into the code, so convert the string to a number before comparison.
and one thing Golang has made inroads is there's only one way to do something.
and ruby is pleasurable because the interface is consistent i.e everything is an object.
As another commenter pointed out [0] this feature existed since 1.4 [1].
Also, I don't understand why so many people seem to be so confused/surprised by it. I can't be 100% sure because it was a long time ago, but I think that a lot of tutorials/introductory articles mention it. Like, "Hey, did you know that Python has this cool syntaxic sugar for this thing? Fascinating!". I think that I learned about chained comparisons long before I wrote my first line of Python.
[0] https://news.ycombinator.com/item?id=42038451
[1] https://docs.python.org/release/1.4/tut/node40.html#SECTION0...
I still don't think it's that confusing. It does what you think it should if you're reading pseudocode, which is after all kind of Python's MO.
if a == b == c != "-": return Winner
?so three empty fields in a row don't "win" the game?
Would have liked to see some humility from the author, especially since he's an interviewer, but instead he deflects the fault onto Python as being confusing, rather than attributing the fault to himself and his own lack of understanding.
Unfortunately, this kind of blame game is par for the course with enginerds. I say this all the time: engineers really should do a sales or customer service job before getting into engineering. This will help you build empathy, which will build your social skills and kindle some humility within you. These traits will give you an edge over other nerds and take you further in your career.
0 < 5 < 100; #True
10 == 10 == 10; #True
The Chaining Binary Precedence is specified for this https://docs.raku.org/language/operators#Chaining_binary_pre...That means you can make your own chaining comparison ops (or overload the current ones) with something like:
multi infix:<~=>(Rat $l, Rat $r) { ($l-$r) < 0.1 ?? True !! False }
is equivalent('<');
Much of Raku such as the default use of Rationals (Rat) is intended to make the language more approachable for non CS experts, so yes: 0.1 + 0.2 = 0.3 # True
There doesn't seem to be a great upside to using it, and adding confusion to the codebase for whatever poor bum has to work on it later is bad karma.
What's interesting to me is the conclusion that it's somehow Python's fault. I wonder if that attitude would work if it came from the candidate.
I think we should be more careful with tests like this. They need to be done with more humility, so it's great that the post was written!