The most copied StackOverflow snippet of all time is flawed (2019)
programming.guide
programming.guide
I think this shows an example of a big problem with StackOverflow compared to its initial vision. I remember listening to Jeff and Joel's podcast, and hearing the vision of applying the Wikipedia model to tech Q&A. The idea was that answers would continue to improve over time.
For the most part, they don't. I'm not quite sure if it's an issue of incentives or culture. Probably some of both. I think that having a person's name attached to their answer, along with a visible score really gives a sense of ownership. As a result, other people don't feel enabled to come along and tweak the answer to improve it.
Then, once an answer is listed at the top, it is given more opportunity for upvotes, so other improved answers don't seem to bubble up. This is a larger issue with most websites that sort by ratings. Generally they sort items based on the total number of votes, including hacker news itself. Instead, to measure the quality of an item, we should look at the number of votes, divided by the number of views. It may be tough to measure the number of views of an item, but we should be able to get a rough estimate based on the position on a page, for example.
If the top comment on a HN discussion is getting 100 views in a minute and 10 upvotes, but the 10th comment down gets 20 views and 5 upvotes, the 10th comment is likely a better quality comment. It should be sorted above the top ranked comment! There would still need to be some smoothing and promotion of new comments to get them enough views to measure their quality as well.
Such a policy on StackOverflow would also help newer, but better answers sort to the top.
Closer, but still not quite what you want probably or a few stray votes can make a massive impact just from discretization effects. What you really care about is which answer is "best" by some metric, and you're trying to infer that as best as possible from the voting history. Average votes do a poor job. Check out this overview from the interblags [0].
[0] https://www.evanmiller.org/how-not-to-sort-by-average-rating...
Yep, definitely. The only challenges there are that there's less literature about doing so and that if you have both up and down votes there's no longer one right way to define a single objective for scoring.
In addition, it's a social engineering problem. At least people with a western psychology seem to respond very strongly when a score is attributed to their person (as opposed to a group success like in a wiki). So you better make the score personal and big and visible, and do not occasionally sort by random just to discover the true score.
If I search for something related to javascript for example, I know there will be a ton of answers for older versions that I am most likely not interested in. However I can only sort by oldest first (related to date).
Old answers are definitely useful a lot of times, but the fact that there's not even the option to sort them the other way around tells me that SO somehow, at it's core, considers new answers less important.
A strange decision if you ask me, considering software changes so much over time.
If anyone has a possible explanation for this I'd love to hear it.
So, I guess the answer to your question of "why can't I" is "good news! you can" :)
More often than not, sorting by "Active", "Oldest" and "Votes" usually surface the same 2 or 3 answers, and I still need to scroll down to the bottom to find out the most recently posted answer that has more up to date info.
I don't see why I shouldn't have the choice to sort by "Reverse Oldest" if you will, when it's so useful a lot of the time.
As someone that's been learning a little JS over the last year, I quickly came to the realization that you skip over the SO links that come up in the search, and you go to one of the many other sites. I've had good luck with w3schools and mdn. SO is a lost cause for JS.
However sometimes I am looking for some error related to a botched nodejs install for example, or something that has to do with permissions being set incorrectly and other stuff that does not live in MDN and other documentation sources.
For the actual language questions I do go directly to MDN instead.
It's worse than that. Edits have to go through a review process that is much more selective and often arbirarily rejects good edits.
What qualifies as "low rep"? I'm easily in the too quintile.
> And no, many more bad edits are accepted, than good edits being rejected. By orders of magnitude.
Do you have any data to support this?
The editing and updating process for stackoverflow is broken and as a direct result I've used the site less and less over the years. Denying the problem just hastens the demise of the site.
I've written, and deleted, several essays on the matter, but a TL;DR: Monica's legitimate questions to staff about a policy got caught up in a crackdown on sealioning-type harassment of trans (etc.) mods in the mod chat, and SO management basically declared war on Monica by mistake. We don't know whether they dealt with the actual harassment (though I think they did, belatedly), because if they did, proper procedure was followed and the perps weren't named-and-shamed in the press.
I think community-based changes to the accepted answer would go a long way to solving your problem too, but it requires someone to be reviewing newer answers and identifying when there's another that would be more appropriate.
It'd incentivise writing newer answers to older questions. Correcting accepted answers that probably weren't ideal to begin with. A new "role" where users hunt through older questions and answers looking for improvements to make.
Stack Overflow answers are supposed to be community-based, but we unfairly prioritise the will of the original questioner *forever*. I don't think that's optimal.
Are you able to point me to a Meta/Blog post or even just a screenshot please? I'd be keen to see it.
Actually it looks like https://meta.stackoverflow.com/questions/405302/introducing-... is the announcement for wanting to tackle the problem. Not sure if they've implemented it yet though.
I don't know if this is still a thing, but for some time in the past when an answer was edited more than a certain amount of times it automatically turned into what was called a "community wiki" answer.
> but the only person who can change an accepted answer is the OP.
This system makes the person arguably _least qualified_ to understand the situation the single arbitrator as to which answer is accepted.Was it the most efficient? First to answer? Copied-and-pasted right in with no integration work? Written by someone with an Indian username? Got the most upvotes? Made a Simpsons reference? Written by someone with an Anime avatar?
Devil's advocate: If it fixed their problem adequately, it's acceptable.
Maybe separate "acceptable" and "ideal" answers would be a nice feature?
What is the argument for the OP being the least qualified?
If anything, they have the most amount of information in this context. I really don't think of them as being the least qualified.
Questions and answers belong to the community. I think the accepted answer should too - maybe after some period of time.
I'd be cautious about overriding an accepted answer. Imagine a situation where there's an easy-to-understand algorithm that's O(n^2) and the "Correct" algorithm that's O(n). If OP only has a dozen datapoints, the former might be the best answer for her specific problem, despite it clearly not being the right approach for most people finding the thread via Google in the future.
There's very little gamification incentive to do so and often the edit queue is full. Still, there are lots of times where important caveats and information is pointed out in the comments and never added to the answer
Interessting. As a random visitor this was something that never came to me from the way SO presents itself.
> For the most part, they don't. I'm not quite sure if it's an issue of incentives or culture.
I think it's more a problem of communication and UI. SO is not really the kind of site that animates people to answer or improve things. The overall design is also more technical and strange, not motivating and userfriendly.
Today for the first time I realized that there is a history for answers and an "improve"-Button that seems to allow me to change someone else answer. I only saw that because I expliciet looked for this because of this thread.
Wikipedia in the beginning was very vocal and motivating to engage all kind people to help and improve articles. SO never had that vibes for me. Additionally, it simply has not the interface that makes it simple to do this stuff. There are only this aweful comments under each answer, which are not really useful to discus an answer in all lenght and from all sides. Might be better to change them to a full fletched forum with some kollaboration editing and some small wiki-functionality or something like that.
I remember they tried to do some kind of wiki with high quality-code-parts, what happend to that?
Classic example of "good is the enemy of best".
This would mean that the same questions would get answered again and again over the years, but I think that could also solve the negative reputation problem of the website.
Two bird with one stone, or if you're Slovenian, two flies with one swat. ^^
Not to mention that the fixed version now has branches as well…
I don't see any loops, but there are a number of branches. The code could probably be generalized using loops to support arbitrary precision, but I think any optimized implementation for a specific precision will have unrolled them.
I wonder how fast it'd be to convert to string and count digits.
When you convert the number to a string you're really transforming it to a decimal format. Which is the domain where you should be solving the problem. Otherwise you're doing some sort transformation in the binary domain and then hopping to pull the answer out of a hat when you do the final convertion to decimal.
Regardless whether they contain a logarithm instruction or not, how may architectures are there these days. Outside of truly embedded computing I can only come up with 2: Intel and ARM. Counting POWER and RISCV is probably a bit of a stretch already.
FYL2X takes two arguments, Y and X, and computes Y log2(X).
FYL2XP1 takes two arguments, Y and X, and computes Y log2(X+1).
As you note, x86 and ARM are by far the most used, and I'd guess that when it comes to Java you are more likely to be running on x86 than ARM, so I figured it was arguable to say "many" when the only one I was sure had a logarithm instruction was x86.
IIRC cmov is actually quite slow. It's just faster than an unpredictable branch. Most branches have predictability so you generally don't want a cmov.
Speaking of which, a couple questions regarding this for anyone who might know:
1. Can you disable cmov on x64 on any compiler? How?
2. Why is cmov so slow? Does it kill register renaming or something like that?
A correctly predicted branch allows the subsequent computation (using of the result of the ?: operator) to start speculatively after waiting only for the relevant input value, without having to wait for the condition or the value on the unused branch. This could sometimes save hundreds of cycles if an unused input is slow due to a cache miss.
There might be a few guys at FAANG who have a planet-scale use case for human readable file sizes. But surely "performance optimising" this is _purely_ code golf geekiness?
(Which is a perfectly valid reason to do it, but I'm gonna choose the most obvious to the next progerammer reading it version over one that 50% or 500% or 5000% fast in almost any use case I can think I'm like to need this... I mean, it's only looking for 6 prefixes "KMGTPE" a six line case statement would work for most people?)
This same property makes CMOV useful in Spectre mitigation, see https://llvm.org/docs/SpeculativeLoadHardening.html
Keeping CMOV slow is now an important security feature.
Speculative execution refers more towards the decoder/uop generation side of the processor (the “in-order” side). A normal “in-order” processor, upon encountering a conditional jump, would wait until the pipeline is finished to check if it should jump or not. It does it by inserting “bubbles” into the pipeline - essentially doing nothing but waiting.
Speculative execution (or branch prediction) would say, “I think the branch will be taken based on X, Y, Z,” and then keep the pipeline full in the process. If the prediction was right, congratulations! You just saved dozens of clock cycles that otherwise would’ve been wasted. If it was wrong, no worries. The pipeline is then flushed; all the speculated instructions’ results are tossed (before they’re “written back”). Then the processor resumes operation on the correct branch.
Speculative execution doesn’t necessitate an out-of-order architecture, and visa-versa. Just a pipelined one. It’s perfectly possible to have an out-of-order architecture that doesn’t speculate, or a speculative one that is completely “in-order”, but they work hand-in-hand, and it makes sense to have both if you have one.
Instead we have overused lambdas and other tricks that started out clever but become a nightmare when wielded without prudence. In this article, the author even points out why not to use his code:
Note that this started out as a challenge to avoid loops and excessive branching. After ironing out all corner cases the code is even less readable than the original version. Personally I would not copy this snippet into production code.
In a nutshell, it's kind of like "prinicple of least priviledge" applied to loops. Maps are weaker than Folds which are weaker than For loops, meaning that the stronger ones can implement the weaker ones but not vice-versa. So it makes sense to choose the weakest version.
More specifically, maps can be trivially parallelized; same for folds, but to a lesser degree, if the reducing operation is associative; and for-loops are hard.
In a way, the APL/J/K family takes this idea and explores it in fine detail. IMHO, for loops are "boring and readable" but only in isolation; when you look at the system as a whole lots of for loops make reasoning about the global behaviour of your code a lot harder for the simple reasone that for-loops are too "strong", giving them unweildy algebraic properties.
Not to mention the performance implications. Parallelisation, composability and system thinking are sometimes overkill and lead to overengineering.
> More specifically, maps can be trivially parallelized; same for folds, but to a lesser degree, if the reducing operation is associative; and for-loops are hard.
In a typical Javascript code reduce operation will not be parallelized. It actually can be slower than a loop because of overhead for creating and calling a function on every iteration.
> when you look at the system as a whole lots of for loops
A code with lot of loops is still more readable than a code with lots of nested reduces.
I think that is a function of familiarity, if you use reduce a lot it will be as easy to read as a loop - perhaps easier because more compact - there is a downside to reading more lines for some people, at some point verbosity becomes its own source of illegibility (although any loop that can easily be turned into a reduce probably won't be excessively verbose anyway)
Of course all that is just on the personal level, you , by using and reading more code with reduce in it will stop finding reduce less easy to understand than loops - but the next programmer without lots of reduce experience will be in your same boat.
In JavaScript, the reduce callback is created once and called repeatedly. For loops are pretty much always the fastest possible way because they use mutable state. They are also a really good way of creating unreadable spaghetti that does things you don't want them to.
I'm not sure what you mean by nested reduces. Chained reduce functions are easy to follow
And they both trivially allow for side effects.
Only if it's actually more readable. The principle of least privilege does not give you any benefit when talking about loop implementations.
> More specifically, maps can be trivially parallelized;
This argument is repeated time and time again but I've never actually seen it work. Maps that can be trivially parallelized aren't worthy to parallelize most of the time. In the rare case it's both trivial and worthy, it's because the map function (and therefore the loop body) are side-effect free, and for those rare cases you don't care too much about the slightly extra effort of extracting the loop body into a function.
> when you look at the system as a whole lots of for loops make reasoning about the global behaviour of your code a lot harder for the simple reason that for-loops are too "strong"
Code is too strong in general. Reasoning about the global behavior of code is difficult if the code itself is complex. Nested maps and reduces will be equally difficult to comprehend. The fact that a map() function tells you that you're converting elements of lists does not save you from understanding what is that conversion doing and why.
Sometimes loops will be better for readability, sometimes it will be map/reduce. Saying that for loops always make it harder to reason about the code does not make too much sense in my opinion.
> The fact that a map() function tells you that you're converting elements of lists does not save you from understanding what is that conversion doing and why.
It can actually, say you have a query that comes in, this calls a function that fetches records from the database, it's not a basic query, it has joins, perhaps a subquery, etc. Then you have another function that transforms the results into whatever presentational format, decorates, wtv, those results, and it's also more than a few basic couple lines of logic.
And now you have a bug report come in, that not all expected results are being shown.
If you have
func does_query -> loop transforms
You have 3 possibilities, the problem is on the storage layer, the problem is on the query, the problem is on the loop.
You read the query, because the bug is subtle, it seems ok, so now you move to the loop. It's a bit complex but seems to be correct too. Now you start debugging what's happening.If you have
func does_query -> func maps_results
You know it's either underlying storage or the query. Since the probability of the storage being broken is less plausible, you know it must be the query. In the end it's a synch problem with something else, and everything is right, but now you only spent time on reproducing the query and being sure that it works as expected.For instance, map - I know that it will return a new collection of exactly the same number of items the iterable being iterated has. When used correctly it shouldn't produce any side-effects outside the mapping of each element.
In some languages now you have for x in y which in my opinion is quite ok as well, but still to change the collection it has to mutate it, and it's not immediate what it will do.
If I see a reduce I know it will iterate again a definite number of times, and that it will return something else than the original iterable (usually), reducing a given collection into something else.
On the other hand forEach should tell me that we're only interested in side-effects.
When these things are used with their semantic context in mind, it becomes slightly easier to grasp immediately what is the scope of what they're doing.
On the other hand, with a for (especially the common, old school one) loop you really never know.
I also don't understand what is complex about the functional counterparts - for (initialise_var, condition, post/pre action) can only be simpler in my mind due to familiarity as it can have a lot of small nuances that impact how the iteration goes - although to be honest, most of the times it isn't complex either - but does seem slightly more complex and with less contextual information about the intent behind the code.
But in a for loop anything can happen- from a map to a reduce to a mix, to whatever convoluted logic the dev comes up with.
But yes - for me
(defn factorial [n]
(reduce * (range 1 (inc n))))
is slightly more readable than def factorial(n):
result = 1
for i in range(2,n+1):
result *= i
return result
I mean in this case the name kinda makes it obvious anyway :)If the operation is conceptually accumulating something over the whole collection and if it's idiomatic in the language I'm using - I will use reduce. Same with map-y and filter-y operations.
But if I have to do some mental gymnastics to make the operation fit reduce - for loop it is. Or generator expression in case of python.
Fold and map in functional languages are often much more restrictive in a sense. For example, with lists, you reduce down a collection [a]->a to a single object, or produce another collection with a map [a]->[a]. So map and fold etc are much more restrictive. That's what makes it clearer.
To be honest this seems to be a familiarity thing > but with reduce you need to know what arguments in a callback mean
If I didn't know for it would be mind boggling what those 3 things, separated by semicolons, are doing It doesn't look like anything in the usual language(s) they're implemented. It's the same with switch.
The only thing both of them have, for and switch, and helps, is that languages that offer it and aren't FP usually use the same *C* form across all, whereas reduce's args and the callback args vary a bit more between languages, and specially between mutable and immutable langs.
I still prefer most of the time the functional specific counterparts.
congruence_classes m l = map (\x -> ((x ==) . (`mod` m)) l) [0..m-1]
than def congruence_classes(m, l):
sets = []
for i in range(m):
sets += [[]]
for v in l:
sets[v % m] += [v]
return sets
For-in is very neat and nice but it still takes two loops and mutation to get there. Simple things are sometimes better as one-line maps. Provability is higher on functional maps too.Same one-liner in (slightly uglier) Python:
def congruent_sets(m, l):
return list(map(lambda x: list(filter(lambda v: v % m == x, l)), range(m)))Ironically, it's a great example of why readability is so much more important than conciseness and one liners.
One idea I have is, that often FP code is not modularized and violates the SOLID principle in doing several things in one line.
there are seldom named subfunctions where the name describe the purpose of the functions- take lamdas as an example: I have to parse the lamda code to learn what it does. Even simple filtering might be improved (kinda C#):
var e = l.Filter(e => e.StartsWith("Comment"));
vs.
var e = l.Filter(ElementIsAComment);
or even using an extension method:
var e = l.FindComments();
sorry I could not come up with a better example- I hope you get my point...
But that much is immediately obvious since it's mapping a filter, that is, has a loop within a loop.
I did consider the second one to also take quadratic time though. I forgot that in python getting list elements by index is O(1) instead of O(n) which is what I'm personally used to with lists.
It's also true that you can replace the filter with
[ v | v <- l, v `mod` m == x ]
but that's not as much fun as(x ==) . (`mod` m)
I just love how it looks and it doesn't personally seem any less clear to me, maybe a bit more verbose.
Have you considered that maybe this is a sign you're too deep into using impractical programming languages?
Cleanness for immutable data structures aside, linked list are a very poor way to store data given the way computer architectures are designed.
“Languages that use ‘list’ for linked lists and have different names for other integer-indexable ordered collections” aren’t necessarily “impractical”.
Even applying it at compile time, it's still O(nm). You have to compute 'v mod m' for each possible value of v and m.
> But that much is immediately obvious since it's mapping a filter, that is, has a loop within a loop.
It's not immediately obvious because you have to parse the calls and see exactly where is the filter and the map.
map(lambda x: do_some_things(x, another_param), filter(lambda x: filter_things(x), lst))
map(lambda x: do_some_things(x, filter(lambda y: filter_things(x, y), another_list)), range(m))
versus retval = []
for x in lst:
if not filter_things(x):
continue
retval.append(do_some_things(x))
and for x in lst:
filtered = []
for y in in another_list:
if filter_things(x, y):
filtered.append(y)
retval.append(do_some_things(x, filtered))
In the first case, you have to parse the parenthesis and arguments to see where exactly are the map and filter cals. In the second, you see a for with a second level of indentation.> I just love how it looks and it doesn't personally seem any less clear to me, maybe a bit more verbose.
It doesn't seem any less clear to you because you're used to it. But think about the things you need to know apart from what a loop, map, filters and lambdas are:
- What is (x ==). Is it a function that returns whether the argument is equal to x? - What is '.'. Function composition? Filter? - Same thing with `mod` m. What are the backticks for?
Compare that with the amount of things you need to know with the Python code with for loops. For that complexity to pay off you need some benefits, and in this case you're only getting disadvantages.
That's the whole point of this discussion. Production code needs to work, have enough performance for its purpose and be maintainable, those are the metrics that matter. Being smart, beautiful or concise are completely secondary, and focusing on them will make for worse code, and it's exactly what happened in this toy example.
def congruent_sets(m, l):
return [[v for v in l if v % m == i] for i in range(m)]If Python were focused on functional programming it would have a utility function for this similar to itertools.groupby (but with indices in an array instead of keys in a dictionary).
itertools.groupby doesn’t return a dictionary, it returns an iterator of (key, (iterator that produces values)) tuples. It sounds, though, like you want something like:
from itertools import groupby
def categorize_into_list(source, _range, key):
first = lambda x: x[0]
sublist_dict = {
k: list(v[1] for v in vs)
for k, vs in groupby(sorted(((key(v), v) for v in source), key=first), first))
}
return [sublist_dict.get(i, []) for i in _range]
Then you could do this with something like: def congruent_sets(m, l):
categorize_into_list(l, m, lambda v: v % m)Unless you're using Perl - "Each element of LIST may produce zero, one, or more elements in the generated list".
But that's just a social convention. There's nothing stopping you from doing other things during your map or reduce.
In practice, the only difference between Map, Reduce and a For loop is that the first two return things. So depending on whether you want to end up with an array containing one item for each pass through the loop, "something else", or nothing, you'll use Map, Reduce or forEach.
You can still increment your global counters, launch the missiles or cause any side effects you like. "using it correctly" and not doing that is just a convention that you happen to prefer.
(and I haven't read the article so not even sure I agree with the example there, this was more in general terms)
a loop that iterates over indices when I want elements is not readable, e.g. I prefer
for element in elements:
rather than for (i = 0 , i < len(elements), i++) { element = elements[i] ...
This is maybe where this aversion comes from, people usually [citation needed] want to iterate over elements, rather than indices.But yes, there are of course cases where the index could be needed, I was merely commenting on the aversion part for generic developers.
Like goto, basic loops are powerful, simple constructs that tell you nothing at all about what the code is doing. For…in loops in many languages are a little better, but map, reduce, or comprehensions are much more expressive as to what the code is doing, but mostly address common cases of for loops.
While loops are weakly expressive (about equal to for…in), but except where they are used as a way (in language without C-style for loops) but there is less often a convenient replacement.
It's the ostrich approach: if you don't see the branches they don't matter.
It prints 1000.0 kB.
> Granted it’s not very readable and log / pow probably makes it less efficient
So, the "improved" solution is both less readable and probably less efficient... where is the improvement then?
The logarithmic approach is harder to reason about, prone to bugs (as proven by this post). I'm baffled at the fact that tons of people considered it a more elegant solution! It's completely the opposite!
[0] https://stackoverflow.blog/2021/04/19/how-often-do-people-ac...
Do the first thing that works, don't overthink it.
In my opinion, the bottom line with obvious caveats is this - Human-time is more valuable than CPU-time.
If you are shipping at scale then the calculus is different - Don't waste end-users' human-time and their cpu-time and/or server's cpu-time.
If you're writing code with a team the calculus is different - Use/Learn techniques and tools to reduce the teams' human-time wastage plus all the above.
If you're writing code just for yourself the calculus is different - Save your own human-time.
I had a few of these cases in my life:
- discovering optimized patterns in Perl, which led to code I could not understand the next day
- discovering decorators in Python, which led to better code
- discovering comprehensions in Python (a magical thing) that led to better code, except when I wanted to be too clever and ended up with Perl-like code
Humans writing code is suboptimal. I can't wait for the day when robots/AI do it for us. I just hope it leads to a utopia and not a dystopia.
As a human, the first thing that I hate about this interpretation of "human readable" format is inconsistency in the number of significant digits. One digit after decimal separator is simply wrong, as when you jump from 999.9 MB to 1.0 GB you go from 4 significant digits to 2, instead it should be 1.000 GB, 10.00 GB and so on. This annoys me enormously when I upload things to Google Drive from Android phone and look at the number of data transferred as as soon it becomes bigger than 1 GB digits stop changing and I become anxious that it stopped the transfer and my Windows Phone nostalgia jumps over the roof (as WP was never infected with this problem by virtue of not using Java, and OneDrive on WP explicitly showed current connection speed, and frozen connection never caused any strange problems with uploaded files like it does on Google Drive on Android).
As a human not from US, the second thing I hate here is lack of locale parameter to pass to formatter as decimal separator is different in different cultures, and in the world of cloud computing the locale of the machine where the code is run is often different from the one where the message is displayed.
As a human from a culture using non latin alphabet, the third thing I hate here should be obvious for a reader.
I had a hard time mentally parsing that sequence even when I knew what your point was so imagine regular users seeing that.
> [...]
> Floating-point arithmetic is hard.
I have successfully avoided FP code for most of my career. At this point, I consider the domain sophisticated enough to be an independent skill on someone's resume.
There is actually no better way, if you try to calculate over the reals (with computers or whatever you want), you are prone to be bitten. Once in a while there's an article about intervalar algebra on HN, those are a great opportunity to just nod positively and remember all of the flaws of intervalar algebra I got to learn on my school's physics labs. (And yeah, those flaws do fit some problems better than FP, but not all.)
It's been a whole field with its own patron saint for a quite a while, take a look at
Numerically unstable algorithms are a problem too but again, intuitively so if you think of the numbers as physical measurements.
These bugs are so subtle and so pervasive that its almost always cheaper to throw more hardware at the problem than it is to hire a numerical analyst. Chances are that you aren't clever enough to unit test your way out of them, either.
It's a matter of time if one doesn't know to look for numerically stable algorithms. Or if one thinks performance merits dropping stability.
https://github.com/RhysU/ar/issues/3 was an old saga in that vein.
The trouble is that people end up using them for any non-integer ("real") numbers. It turns out that in modern times scientific calculations with measured values are not necessarily the bulk of calculations in actually written software.
In the 21st century, i don't think there's any good reason for literals like `21.2` to represent IEEE floats instead of a non-integer data representation that works more how people expect for 'exact' numbers (ie, based on decimal instead of binary arithmetic; supporting more significant digits than an IEEE float; so-called "BigDecimal"), at the cost of some performance that you can usually afford.
And yet, in every language I know, even newer ones, a decimal literal represents a float! It's just asking for trouble. IEEE float should be the 'special case' requiring special syntax or instantiation, a literal like `98.3` should get you a BigDecimal!
IEEE floats are a really clever algorithm for a time when memory was much more constrained and scientific computing was a larger portion of the universe of software. But now they ought to be a specialty tool, not the go-to for representing non-integer numbers.
There are still a lot of people doing a lot of work in which they hardly ever want a floating point number but end up using it because it's the "obvious" one that happens when you just write `4.2`, and the BigDecimal is cumbersome to use.
1 - quantity2 / (quantity1 - quantity2)
... or some such thing. If quantity1 and 2 are similar, ouch!
quantity2/quantity1 - 1
or
(quantity2 - quantity1) / quantity1
with double precision and physically reasonable values.
Sadly, I suspect too many "computer science" courses have turned into "vocational coding" courses, and now those people are computing summary statistics on large datasets in Javascript...
A large dataset means lots of values, maybe we can assume the number of values is way bigger than any individual value. Perhaps think of McDonalds purchases nation-wide: billions of values but each value is probably less than $10.
The simplest summary statistic would be a grand total (sum). If you have a good mental model of floats, you immediately see the problem!
The mental model of floats which I use is 1) floats are not numbers, they are buckets, and 2) as you get further away from zero, the buckets get bigger.
So let’s say you are calculating the sum, and it is already at 1 billion, and the next purchase is $3.57. You take 1 billion, you add 3.57 to it, and you get... 1 billion. And this happens for all of the rest of the purchases as well.
Remember: 1 billion is not a number, it is a bucket, and it turns out that when you are that far away from zero, the size of the bucket is 64. So 3.57 is simply not big enough to reach the next bucket.
It was precisely this problem. The individual had done all data preparation/normalization in 32-bit because the model training used 32-bit on the GPU. It's a very reasonable mistake if one hasn't been exposed to floating point woes. I was pleased to see that the individual ultimately caught it when observing that 2 libraries disagreed about the mean.
Computing a 64-bit mean was enough. Compensated (i.e. Kahan) summation would have worked too.
Seems like the loop based code wasn't so bad after all...
Something like "cherry pick this answer, with attribution, and notifications when flaws and/or improvements are found".
Maybe that's a terrible idea (there's definitely risk involved, and the potential to spread and create bad software), but equally I don't know why it would be significantly worse than unattributed code snippets and trends towards single-function libraries.
Mostly but not entirely because NPM handled things poorly in various ways.
Good thing it wasn't a range check function. I hear those are expensive.
I don’t find it ironic, I find it quite normal that even small snippets of code contains bugs (given the daily review requests I receive).
I think when copying code literally from StackOverflow what’s more important is understanding what the code does, and why , rather than copying it ad-verbatim by copy & pasting it into your production code.
I also often find on StackExchange et al that quite often the most upvoted is the one that ‘fixes it’ for ‘most people’ yet the correct answer is down at number 3 or 4. Again, understanding the answer and why it applies, helps give you the context to understand if this is actually the solution to your problem or just treats the symptom.
It's a pretty neat rule to have in mind.
I didn't really grok Test Driven Development until I worked thru the book, line-by-line, experiencing the workflow.
Knowledge vs experience.
And also first, or at least early, and subject to a reinforcing cycle of 'sufficiently good' or 'fixed it enough' that it achieves stratospherically more votes than an 'even more good' or 'fixes it properly' answer that came in too late for the same traction.
So exactly the solution most project managers are after? /s
I think you're right that online scoring systems tend to incentivise false confidence. This happens with blog posts too, where a student of some topic writes a confident and subtly incorrect blog post, and it then ends up on the HN front-page. Only someone with a relatively deep knowledge of the topic can then call out the errors. Ideally it should always be made clear upfront that the author is new to the material.
Somewhat related: Stack Overflow's unfortunate norm of calling out mistakes in answers in a way that goes beyond confidence and strays into condescension and borderline hostility. For a lot of people it seems it's not enough to be seen to be right, they also feel the need to paint someone else as clueless, while just about passing as acceptably polite by keeping the aggression passive. If challenged, they'll brush it off as 'directness'.
> Our sites are all intended to be a sort of representative democracy. Moderator elections are an important part of that plan, but voting on questions and answers is the primary mechanism through which the community governs the site on a day to day basis.
Meh, I'm usually there looking for how to do something, and if a response helps me do whatever I was looking to acomplish, or at least on the right track, it was helpful and worth an upvote. I've never upvoted just because someone sounded confident.... at least not on SO.
They may not have been the attention seekers like other posters. But they provided exactly what was asked for. And when I come across their post years later I upvote.
Maybe, but sounds like it's merely the Java snippet from SO found most often on github. Not sure why blog author didn't include the word "Java" in his title or the first paragraph:
> an answer I wrote almost a decade ago was found to be the most copied snippet on Stack Overflow
There is no evidence for this claim in the blog post, just that it's the "most copied Java snippet". And it's just based on occurrences in github. Maybe the most-copied snippet is an AWK or ffmpeg one-liner? Something that wouldn't find its way into a github repo. Or maybe something undetectably vanilla, like answers to "How do you write loops in language X?" Is there a way of finding out what actually is the most-copied snippet?
>>> math.log(1000)/math.log(10)
2.9999999999999996
>>> int(math.log(1000)/math.log(10))
2
But I don't know about the guarantees provided in the JavaScript standard (or more importantly those offered by actual browsers).In this case, there's only about six boundary cases to consider so you can just manually verify it works as expected.
The weakness of your approach is that no one’s judgment is sound 100% of the time.
Alternatively, folks who always prioritize correctness may occasionally “waste their time”, but two things to consider: 1) their judgement is no longer an issue, and 2) in the long run they have spent more time training their correctness muscles and are in better shape.
Sure, the code doesn't do exactly what the programmer wanted. But that doesn't necessarily make it incorrect.
https://stackoverflow.com/a/40429822/864112
It boggles the mind that anyone could ever suggest this as a solution.
etc
EDIT: Apparently the answer was edited by another user just recently making the clarification.
Shame about the hostile reaction from others towards your question. Keep asking questions (especially things that are presented without comment), and don't be afraid that doing so will make you look stupid or that you should feel like you should be punished for it.
0. https://blog.imgur.com/wp-content/uploads/2017/06/mocking.jp...
I agree that your parent is a good question that should have been well received, but, as far as I can tell, it was. Where do you see a hostile reaction? In fact the only hostility I see in this thread is what you directed at Python, which, in this context, seems unmotivated; it is surely true that one can write code in any language whose full import isn't immediately apparent. At the moment, the only other response to your parent is from hvdijk, saying (https://news.ycombinator.com/item?id=27534803):
> The question was how to build a URL, not how to send off a request to it. The answer sends off a request and then inspects the response to see what URL was used. If you wanted to send off the request, inspecting the URL on the result is probably not useful. If you didn't want to send off the request, doing it this was is wasteful or even harmful.
This seems like a response that takes the question seriously and addresses it clearly, just as it should.
That comment is, just like the downvotes the question received, precisely the sort of thing that discourages asking honest questions rather than welcoming them. Note that by the way it is written it, too, assumes that it is both obvious and understood that Python's request.get will "send off a request"—instead of merely building a request and returning it to you. Rather than just straightforwardly answering the question (by explaining what this part of the Python' standard library is actually doing—which is the relevant missing piece here, and which no one should be expected to know) the quoted comment ("The question was how to build a URL, not how to send off a request to it") pins the misunderstanding on the questioner by tacitly implying the questioner isn't paying attention to something else entirely different.
The comment, when considered in full and in context, actually has the effect of subtly discouraging/admonishing the questioner (and likeminded people with the same question) for failing to recognize something that is, to the sophomoric Python crowd, obvious and worthy of ridicule—which is what maest's thread was all about, by the way (and almost certainly why it got moderated).
Good.
> This is akin to answering "how do I bake a cake?" with "open up a bakery, walk inside, and ask for a cake"
Only it's more like answering "how much does this cake cost" by purchasing the cake and looking at the receipt.
Or should I say... analogies are like Uber, but for metaphors. No. I should stop before writing that.
Except the java.net.URL.equals and java.net.URL.hashcode methods do almost the same thing: they issue DNS requests (!)
"Two hosts are considered equivalent if both host names can be resolved into the same IP addresses; else if either host name can't be resolved, the host names must be equal without regard to case; or both host names equal to null."
See https://docs.oracle.com/en/java/javase/11/docs/api/java.base...
There is a bug raised[1], but it can't be fixed for backwards compatibility reasons.
I'll never forget this now, after debugging a very horrible and severe and very intermittent performance issue in some code over 20 years ago. A (slow) DNS resolver occasionally caused 1000x performance degradation on remote sites. That was horrible to work out.
[1] https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4434494
What's missing still is a comprehensive set of test cases to check against.
If such cases were spec'ed to go along with the original code, fthen at least one could have seen the applicability range and perhaps other people would have added some challenging corner cases (just as mentioned in the OP).
I wonder whether the author is suggesting that (potentially) nine branches is a small number, or they overlooked ternary expressions and function calls and are just counting the if statement.
This. Boggles.
(Of course, this static method exists in Apache Commons, going back at least 20 years. But the fellow "code golfers" of the author voted someone to the first answer who similarly had the irresistible urge to try to be very clever. It's a scourge on StackOverflow.)
function prettyPrintBytesSI(x: number): string {
const magnitude = Math.abs(x)
if (magnitude < 1e3) return `${x} B`
else if (magnitude < 1e6) return `${x/1e3} kB`
else if (magnitude < 1e9) return `${x/1e6} MB`
else if (magnitude < 1e12) return `${x/1e9} GB`
else if (magnitude < 1e15) return `${x/1e12} TB`
else if (magnitude < 1e18) return `${x/1e15} PB`
else return `${x} B`
}This is for presenting stuff in a user interface. Who cares if you can find some weird edge case using MAX_LONG and MAX_DOUBLE which never will occur in practice.
And no - the edge cannot occur as it would require a file / whatever to have that size.
I betcha that is going to give you more bugs than the edge cases discussed here.
0: https://en.wikipedia.org/wiki/International_System_of_Units#...
That there then are numeric stability issues and a pretty gross fudge factor is used to fix them worsens the situation. I would have a quiet word with any programmer I worked with that came up with this "solution".
"BKMGTPE". n==0 => 'B'; n==4 => 'T'.
810 has 3 digits. n==0, 810B.
999950 has 6 digits. n==1, you've got 999.9K
1100000 has 7 digits. n==2, 1.1M
1234567890 has 10 digits. n==3, 1.2G
https://news.ycombinator.com/item?id=21698619
Still, a good lesson!
Their proposed improvement is also terrible, since it divides multiple times unnecessary, and checks for negativity multiple times unnecessarily.
The proper simple solution is of course a handwritten binary search with if-else blocks that starts with the most likely range, annotated with "likely" annotations, and a single division.
If this is the main task of the program for a while, and thus a large fraction of the cache can be dedicated to it, then solutions with large lookup tables are worth trying (obviously optimizing string formatting is also essential in this case).
This is why software is so often broken, there's a lot of incompetent people programming.
That's not a fair statement.
Good programmers are programmers who deliver value - who build robust, maintainable features in reasonable time that address user needs.
Whether or not you would quickly find the correct approach to this specific problem is a miniscule, pedantic detail in a giant ocean of programming skills and experiences.
About your "proper simple solution": I don't think that's a good idea either. Based on your next paragraph, the version you suggest with the handwritten binary search and "likely" annotations is for the case where the code isn't performance critical: for where the code is performance critical, you suggest a different solution. If the code isn't performance critical, please do not turn it into an unreadable mess over what would become a negligible overall performance gain. Write it in a simple, obviously correct way, keep it boring, and you'll keep it stable; you can use the time you save on fixing bugs in your super optimised version on improving more critical parts of your program.
A real number type could be bounded by the amount of RAM you have.
I've ironically found that big integer libraries sometimes optimize math routines more than string conversion. This was quite annoying for me when I optimized the factorial function in Ruby, and found that generating the string was my bottleneck. I then optimized that as well. :-)
A quick Google says there's an estimate of 10^78 to 10^82 atoms in the universe. That number would be able to be stored in well under 300 bits.
Lots of problems suffer from 'combinatorial explosion' [1].
I recently learned about the Archimedes's cattle problem, the solution is of order 10^206544 [2]
[1] https://en.wikipedia.org/wiki/Combinatorial_explosion
[2] https://en.wikipedia.org/wiki/Archimedes%27s_cattle_problem
The author just thinks a completely unreadable (but supposedly faster) variant using logarithms is "better" than the simple loop used in the original snippet?
Write your code for junior devs in their first week at your company, not for academic journals.
However he notes:
> FWIW, all 22 answers posted, including the ones using Apache Commons and Android libraries, had this bug (or a variation of it) at the time of writing this article.
Very few people I have encountered have complained about code being 'too simple' or 'too readable', but the opposite happens on a near daily/weekly basis.
Write comments, use a for loop, avoid global state, keep your nesting limited to 2-3 levels, be kind to your junior devs.
He isn't trying to get people to use the log version.
The log approach _is_ the most copied snippet.
- the first answer posted on SO was a simple loop
- the author posted a 2nd (supposedly faster but less readable) answer. The author didn't think this answer was better than the loop, but it seems the community did and it became accepted (and extremely popular). THIS is the version that was buggy.
The author later went back and fixed their own buggy version.
So yes there's an argument to be made that the very first simple loop was better, but that's orthogonal to the point of the story.
Hard and fast rules about coding style are silly. There's a time and place for clever code, and there's a time and place for verbose and straightforward code.
I write performance-critical code. Juniors shouldn't be mucking about there, because it's performance critical. I also write non-performance-critical code with some effort. I write that stuff for the juniors.
When writing for academic journals, it looks like the stuff I write for juniors. I'll drop a hint here or there so experts can reproduce less-obvious optimizations.
For an application area where this applies, consider a web-based game. Using JavaScript keeps you from shipping another application. But occasionally you may have bit twiddling and/or byte munching needs. Which you need to do in JavaScript.
The question being answered clearly wanted base2 engineering prefix units, rather than the standard base10 engineering prefix units.
suffixes = [ "EB", "PB", "TB", "GB", "MB", "KB", "B" ]
magnitudes = [ 2^60, 2^50, 2^40, 2^30, 2^20, 2^10, 2^0 ] // Pseudocode, also 64 bit integers required. (Compilers might assume unsigned 32 for int)
The author's code gives an option for the units:
int unit = si ? 1000 : 1024;