7 ms·
That is the issue I have with it: ‘an afternoon’ is already way too vague. Make it ‘5 minutes’ (left pad etc) then I think it works out.
by tluyben2 6y ago
That is the issue I have with it: ‘an afternoon’ is already way too vague. Make it ‘5 minutes’ (left pad etc) then I think it works out.
- axlee 6y agoIs that really 5 minutes? (For when left-pad was relevant) var cache = [ '', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' ]; function leftPad (str, len, ch) { // convert `str` to a `string` str = str + ''; // `len` is the `pad`'s length now len = len - str.length; // doesn't need to pad if (len <= 0) return str; // `ch` defaults to `' '` if (!ch && ch !== 0) ch = ' '; // convert `ch` to a `string` cuz it could be a number ch = ch + ''; // cache common use cases if (ch === ' ' && len < 10) return cache[len] + str; // `pad` starts with an empty string var pad = ''; // loop while (true) { // add `ch` to `pad` if `len` is odd if (len & 1) pad += ch; // divide `len` by 2, ditch the remainder len >>= 1; // "double" the `ch` so this operation count grows logarithmically on `len` // each time `ch` is "doubled", the `len` would need to be "doubled" too // similar to finding a value in binary search tree, hence O(log(n)) if (len) ch += ch; // `len` is 0, exit the loop else break; } // pad `str`! return pad + str; }
- TheDong 6y agoOne of the points of the article is that when you code a dependency for your purpose, you get smaller code. For example, I know I'll always pass it a string, so I don't need to type-check. I started with the problem of "I need a function that pads a string with a character out to some length". Coding it up took under 1 minute, easily under 5 minutes. function leftPad (str, len, ch) { const neededPadding = len - str.length; if (neededPadding <= 0) { return str; } return ch.repeat(neededPadding) + str; } It took me longer to write this comment. The fact that the left-pad code has optimizations (which don't matter for the place I'm using it) and type-checks (which don't matter; my higher level unit tests would catch that mistake) is beside the point.
- scarface74 6y agoUnless you are doing front end code, why does the size of the code matter?
- TheDong 6y ago"smaller" there was short-hand for "more focused, purpose-built to your problem, less generic". All of those properties make the code easier to reason about and test. Being easier to reason about, and being a more exact abstraction for my needs, are both incredibly valuable properties.
- scarface74 6y agoMore generic is better. If I go into a codebase that uses standard libraries. Even if I don’t know how to do something about it someone on the Internet does. Your custom framework - not so much. I don’t have to care about how the underlying libraries work. I can treat them as a black box.
- TheDong 6y ago> I don’t have to care about how the underlying libraries work. I can treat them as a black box. When I wrote haskell, the majority of the libraries did just work, and I didn't have to dig into their code to find bugs often. When I wrote javascript, hundreds of the libraries I used did not just work. I usually had to care very much about their details because they were poorly implemented, full of bugs and incorrect abstractions, and often abandoned soon after. I agree that there's benefits in reusing some well-socialized and well-implemented generic frameworks and abstractions. It's not worth using generic abstractions that are not well understood, buggy, and don't match your needs closely. In that case, write your own. More generic is not always better. Above, I'm arguing that it's important for code to be easier to reason about. If a generic abstraction helps with that, cool, but it's not always going to be the case.
- scarface74 6y ago
- deleted 6y ago[deleted]
- cogman10 6y agoThis is actually a great example of why you SHOULDN'T use left pad. I've not profiled it, but I'm going to guess that now-a-days this will be faster than the current implementation on npm. function leftPad (str, len, ch) { str += ''; len = len - str.length; if (len <= 0) return str; if (!ch && ch !== 0) ch = ' '; ch += ''; return ch.repeat(len) + str; } Why? because the VM is (very likely) going to do exactly what the cache would have done. It can replace `ch.repeat(len) + str;` with a presized string allocation and a memcpy of ch + str characters.
- goldenkey 6y agoThis code checks for ch !== 0, yet the bypass branch afterwards calls .repeat. The Number type in JS has no repeat method. Static typing would actually fix this kind of coding error.
- reificator 6y ago> This code checks for ch !== 0, yet the Number type in JS has no repeat method. > Static typing would actually fix this kind of coding error. That's not a coding error, that's addressing an oddity of JS coercion rules that a less experienced developer could easily have missed. > if (!ch && ch !== 0) ch = ' ' That code says that if `ch` is falsey and not equal to 0, then set it to a space. The only arguable falsey value that should be excluded here is a literal `false`, but that's not a single character and is fairly ambiguous either way. I'd certainly fall on the side that a literal false should not be converted to `'false'` here. > ch += ''; The next line converts to to a string by adding it to the empty string. > return ch.repeat(len) + str; So by the time it gets to this line we know ch is a string. Static typing is great, but the bug you claim is there is not actually there.
- cogman10 6y agoFYI, I edited the post after the bug was pointed out to eliminate it and bring this method up to parity with leftpad.
- 6y ago
- reificator 6y agoYes, particularly when you know the types that will be passed in. True that typechecking in JS can be a little timeconsuming, so it might take 10-15 for me to write a general purpose left pad. Also I seem to remember that someone benchmarked the cached version and found it to be slower than the naive approach anyway. I could be mistaken there.
- ghj 6y agoThis is so hilariously overengineered: not failing when passed the wrong types, arbitrarily caching padding of len < 10, etc The most egregious is the bad big O analysis for a pointless binary search. The loop does indeed run O(log(n)) times but `ch += ch` still takes O(ch.length) which is growing exponentially. It ends up being a complicated way of still taking O(n) time while creating a lot of intermediate strings. It isn't any faster than just creating the padding with a loop or `new Array(len).fill(ch).join('')` or `ch.repeat(len)`
- Ajedi32 6y agoDid you run benchmarks to validate those assumptions? The developers of left-pad did: https://github.com/left-pad/left-pad/blob/master/perf/perf.js https://github.com/left-pad/left-pad/blob/master/perf/perf.j... It's not overengineered if thousands of downstream projects are relying on it, some of which might see significant benefits from those performance optimizations.
- cogman10 6y agoToo bad that's a flawed benchmarking methodolgy. JITs are notoriously hard to correctly profile and the benchmark lib isn't even sort of doing the right thing. For example, it's missing warmup. The results aren't being consumed in a way that wouldn't optimize them away. The framework itself imposes a pretty large amount of overhead (moreso than I'd expect from leftpad). It is somewhat likely that what they are measuring isn't leftpad performance, but rather how fast the JIT ends up optimizing the benchmark code. I'd suggest watching this video https://vimeo.com/78900556 https://vimeo.com/78900556 It's about microprofiling the JVM, but the principles are the same for other JIT based languages (such as javascript).
- Ajedi32 6y agoYeah benchmarks can be much more difficult to get right than it might seem at first glance. Good thing they didn't try to write their own benchmarking code, otherwise they might have fallen into those traps you just mentioned. Luckily, they didn't, and instead pulled in the the `benchmark` library as a development dependency. The author of said library works on V8, and already considered all those problems and much, much, more[1]. [1]: https://mathiasbynens.be/notes/javascript-benchmarking https://mathiasbynens.be/notes/javascript-benchmarking
- tluyben2 6y agoWell, my point was more that if a programmer thinks '5 minutes work' it's often 10+ minutes; so when a programmer thinks 'an afternoon', you can possibly lose a week. And then the article really doesn't work. And yeah, I would and do write leftpad myself it it's not in the stdlib. But if there is a large library full of similar (string) functions that I might need, I would include that library. Not a singular dependency for this type of function.
- mjevans 6y agoThe issue I have with this is a lack of specification. Left pad _what_? Numbers or ASCII-only-printing? OK that's a reasonable. Is there a desired overflow behavior? Past that it becomes more an issue of where and why. The suddenly not-trivial example includes questions about fonts, layout, and multi-byte characters. Emoji, etc. Incidentally, in pseudoscope: Create a valid full-space pad string (termination / etc), then decrement back from the end of the source string and over-write the pad characters from the end to the start of the string, exiting either on no more pad characters or no more input. A second algorithm might combine those two steps as one pass, fill the output buffer from back to front. Only for C style strings would this be an issue given the dynamic end point for the data structure.