If you were around in the 80's and 90's you might have already memorized the prime 8675309 (https://en.wikipedia.org/wiki/867-5309/Jenny). It's also a twin prime, so you can add 2 to get another prime (8675311).
HN user
stevelosh
Move slow and make things.
That quote is from https://berthub.eu/articles/posts/immune-system/
Could have also seen it via https://jsomers.net/i-should-have-loved-biology/ which quotes it directly
I've been using it during the beta and plan on paying for it once they start charging. Previously I had tried switching to DuckDuckGo, but found myself using !g half the time because I knew the DDG results would be garbage compared to the Google results.
I haven't found Kagi's results to be significantly better than Google's results, but unlike DDG they're not hugely worse than Google. It's all the extra stuff that makes me want to use Kagi instead of Google:
* Ability to banish sites from results forever. E.g. I never want to see the Concordia Lutheran High School's website when I search for things in the Common Lisp HyperSpec with clhs.
* Ability to weight sites higher. E.g. there are two mirrors of the Common Lisp HyperSpec that appear in results, one at http://clhs.lisp.se and one at http://www.lispworks.com/documentation/HyperSpec/. The lisp.se mirror is hilariously slow for me (5-10 seconds to load) while the lispworks one is instant. Google always ranks lisp.se first which tricks me into clicking it and wasting my time. Kagi lets me weight that site lower so the lispworks site comes first. Banishing lisp.se would also work, but just upweighting it means I can still find it if the other mirror ever goes down or something.
* I reported a minor bug to Kagi and they fixed it within a few days. I doubt that would happen with Google.
* Supports DDG-style bang queries, which I use occasionally.
* No goddamn ads/tracking (I use uBlock Origin, but it's the principle of the thing). I've almost entirely de-Googled my life at this point (Youtube is the only holdout for me).
So it's not that it gives better results than Google, it's that the results aren't any worse and the extra quality-of-life features are really nice.
Alternate source, with fixed formatting: https://gist.github.com/mjdominus/3841427
Common Lisp isn't that esoteric!
(defun fun-reader (stream arg)
(declare (ignore arg))
(read-delimited-list #\⸨ stream t))
(set-macro-character #\⸨ (get-macro-character #\) nil))
(set-macro-character #\( #'fun-reader)
(defun square (x⸨
(* x x⸨⸨
(square 10⸨
; => 100NES tetris and modern tetris are VERY different games. NES tetris didn't have hard drop, hold, SRS, 7-bag randomizer, or multiple piece previews, all of which are in all modern guideline tetris games and can help you go faster. Tetr.io and Jstris go even further than guideline tetris: there's no line clear delay and you can set DAS/ARR[1] to anything you want, which lets you go even faster.
The bottleneck isn't the keyboard input. Pretty much all top players use ARR 0 and 2-step finesse[2] (or something close to it), which means you can position any piece where you want in 2 keystrokes at most, plus 1 more to hard drop it. And certain stacking styles (e.g. 6-3 stacking[3]) lend themselves well to not requiring the full 2 keystrokes pretty often, so in practice you end up with ~2.6 keys per piece instead of 3. A typical DAS for a pro player (e.g. someone like Firestorm) might be somewhere around 70ms. So if we roughly estimate 100ms average per piece (70ms to DAS, with some extra for the sometimes-needed rotation and hard drop), that would be ~10 pieces per second, which is well over the ~6.5 PPS in the record sprints.
The physical pressing of the keys also isn't really a bottleneck. Keys per second in the top sprints is ~16. I just mashed my movement/rotation keys into `time | wc -c` and got ~35 KPS after 2 beers, and I don't even have a particularly gaming-focused keyboard.
The bottleneck also isn't reaction times. Someone might trot out a hastily-googled 250ms as a typical human reaction time and claim 100ms per piece isn't possible, but good players aren't reacting to each piece as it comes -- they're using the previews (and hold) to react to the piece that's coming 4-5 steps in the future, which is closer to a second away.
It's really just about how fast you can process the upcoming queue and still stack cleanly.
[1] https://harddrop.com/wiki/DAS [2]: https://four.lol/mid-game/finesse [3]: https://harddrop.com/forums/index.php?showtopic=2985
WR for 40L is 14.9 by Reset[1] last I heard. 15 is definitely humanly possible.
[1] https://twitter.com/jstrisgame/status/1424548438096683011
If your goal is to force a touch to decrypt passwords, https://www.palkeo.com/en/blog/perfect-password-manager.html works. You can keep the GPG private key on a Yubikey and require a touch to decrypt.
The user might copy a block of text with multiple URLs, all of which get pulled out by the grep.
The golang one also silently drops rows.Err() on the floor.
Paper: https://www.sciencedirect.com/science/article/pii/S0022202X1...
For instance, in our work, we noted that the algorithm appeared more likely to interpret images with rulers as malignant. Why? In our dataset, images with rulers were more likely to be malignant; thus the algorithm inadvertently “learned” that rulers are malignant.
Postmodern can do the right thing if you use prepared queries. See `defprepared`.
You can set this up with `pass` and a Yubikey: https://www.palkeo.com/en/blog/perfect-password-manager.html
However, the CL type system is very much lacking compared to Rust or Haskell. I'm hoping one day someone will make a statically, strongly typed Lisp that still doesn't sacrifice its flexibility and expressive power. I'd jump to that in an instant.
https://github.com/stylewarning/coalton looks promising, and stylewarning has recently said he's still working on it.
I don't use/like Roswell, so I'd really prefer a .deb if at all possible.
Thirded. I would also add Clasp to my projects' list of implementations to run unit tests on[1] if I could easily install it on Ubuntu with a .deb.
[1]: e.g. adding a test-clasp clause to https://github.com/sjl/cl-digraph/blob/master/Makefile#L14-L...
Are there any other languages that change as slowly as C?
ANSI Common Lisp has had the same spec since 1994.
(This is not a problem, because Common Lisp gives you enough high and low level power to do (almost) whatever you want in user space, instead of relying on the language designers to add a particular feature.)
`n` is the position in the sequence:
n=1 = (mod (* 1504170715041707 1) 4503599627370517) = 1504170715041707
n=2 = (mod (* 1504170715041707 2) 4503599627370517) = 3008341430083414
n=3 = (mod (* 1504170715041707 3) 4503599627370517) = 8912517754604I guess years of using single page Javascript webapps where the back button is a complete shitshow has trained me to not even consider trying it. I'm impressed it actually works on Google Maps.
I just recorded a two minute video me of me trying to do this. Maybe it will help you understand why people like Gravis (and myself) are frustrated. The video is at http://paste.stevelosh.com/1983.webm but it's a little blurry, so I'll narrate it.
(There's a less blurry mkv at http://paste.stevelosh.com/1983.mkv for those that want it.)
I go to `maps.google.com`. The page loads a search box, with my cursor focused inside it. Then it unfocuses the search box while some other boxes pop in. Then it refocuses the search box. Is it done thrashing? Can I type yet? I wait for a few seconds. I would have already entered in my query by now in 1983. I sigh. This bodes well.
I guess it's as done as it's ever gonna be. I search for "rochester ny to montreal qc". I wait for the screen to load. It finds me a route, which is actually good. Step one done.
Now I want to find a restaurant somewhere in the middle. Let's try just browsing around. I find somewhere roughly in the middle — Watertown seems like a good place to stop.
I zoom in on Watertown. I wait for the screen to load. I look around the map and see some restaurants, so I click one. Now I want to read the reviews, so I scroll down to find the "See All Reviews" link. My scroll wheel stops working after I scroll more than an inch or two at a time, until I move it out of the left hand pane and back inside it. I sigh, wiggle my mouse back and forth repeatedly to scroll down and click on the link.
A whirl of colors — suddenly the map zooms in on the location. Why does it do this? I wanted to read the reviews, not look more closely at the map! Now that the map is zoomed in, a hundred other points of interest are suddenly cluttering the map. I wanted to read reviews about this restaurant, and suddenly 3/4 of my screen is filled with text about other places. I sigh.
I ignore the garbage now cluttering most of my screen and read some reviews. This place seems fine. I click the back arrow, then click Add Stop to add it to the route. I wait for the screen to load. Suddenly my screen whirls with color and zooms out, losing my view of Watertown. I sigh.
My trip is now 8.5 hours instead of 5.5, because it added the new stop at the end. AlphaGo can win Go tournaments, but I guess it would be too much to ask for Google to somehow divine that when I add a stop in the middle of a 5.5 hour trip, I might want to visit it on the way by default. I sigh and manually reorder the stops.
Let's also find a gas station somewhere before Montreal, because I like to get gas before I get into the city so I don't have to deal with it once I'm in. Cornwall seems like a good place to stop.
I zoom in on Cornwall. I wait for the screen to load. I don't see any gas stations markers, but that's fine, there's a button that says "Gas stations" on the left! I click it and the screen goes blank. I wait for the screen to load. I've suddenly been whisked away to downtown Montreal instead of looking around where I'm currently centered on the map. Guess I should have read the heading above the buttons first. I sigh.
I click "back to directions". I wait for the screen to load. The map does not return to where I was previously, it just zooms to show the entire route, throwing out my zoomed-in application state. I think back to Gravis' tweet of "gmaps wildly thrashes the map around every time you do anything. Any time you search, almost any time you click on anything" and I sigh.
I rezoom in on Cornwall. I wait for the screen to load. The gas station button didn't work, but surely we can search, right? I don't see a search box on the screen, so I roll the dice and hit Add Destination. This gives me a text box, so I try searching for "gas stations" and pressing enter. This apparently didn't search, but just added one particular gas station to the route. It also zoomed me back out, throwing away my previous zoomed in view.
I rezoom in on Cornwall. I wait for the screen to load. I notice the gas station it picked happens to be across the US/Canada border from the route. That clearly won't work. I sigh and remove the destination. This zooms me back out (I wait for the screen to load), throwing away my previous zoomed in view.
I rezoom in on Cornwall. I wait for the screen to load. I click Add Destination again and this time notice that when my cursor is in the box, there's a magnifying glass icon — the universal icon for "search" — right next to the X icon (which will surely close the box). It even has a tooltip that says "Search"! Aha! That was well-hidden, UI designer, but I've surely defeated you. I click the magnifying glass icon and it… closes the input box. I… what? I sigh, loudly. It has also zoomed me out, throwing away my previous zoomed in view. I wait for the screen to load.
I rezoom in on Cornwall. I wait for the screen to load. Okay, apparently I can't search to try to find routes. I guess I'll resort to browsing around the map again. I notice what looks like a gas station called "Pioneer" and click on it. Cool. But then I realize this is on a bit of a side street. Surely I can find a gas station along the main road. Let me just cancel out of this location by pressing X.
My entire route is completely gone. All that time I just spent, flushed down the toilet. To add insult to injury: this is the one time that it didn't automatically zoom me out and lose my view of the map. It just threw away all of my other state.
Fuck this. I'm with Gravis.
This would save me literally thousands of lines of code at my day job:
(defmacro ?= (symbol-or-symbols expr)
(let* ((symbols (etypecase symbol-or-symbols
(cons symbol-or-symbols)
(symbol (list symbol-or-symbols))))
(tmps (loop :for s :in symbols :collect (gensym (symbol-name s))))
(err (gensym "ERROR")))
`(multiple-value-bind (,@tmps ,err) ,expr
(if (not (null ,err))
(return ,err)
(setf (values ,@symbols) (values ,@tmps))))))That's also probably not the best example, since that's also trivial if you have an editor supporting multiple cursors.
Yeah, it's hard to come up with a decent example because I don't even think about it any more, it's just burned into my fingers. I just know how excruciating it feels to edit without Vim (I even wrote that last comment in Vim and then copied it into the browser because writing it in a text field is miserable).
You're talking about how it's easy to move to a function or a file, and then you're "ready to edit", but edanm is talking about actually doing the editing.
For example: say I jump to this function and want to fix a bug (the `search` should strip off the first two characters here, not just one). So I need to change `regex.search(s[1:], ...)` to `regex.search(s[2:], ...)`:
def compile_hg_glob(line):
pat = glob_to_re(line)
# Mercurial ignore globs are quasi-rooted at directory boundaries or the
# beginning of the pattern.
pat = '(^|/)' + pat
# Mercurial globs also have to match to the end of the pattern.
pat = pat + '$'
try:
regex = re.compile(pat)
return lambda s: regex.search(s[1:] if s.startswith('./') else s)
except:
warn("could not parse hgignore pattern '%s'" % line)
return lambda s: True
If I've just used a fancy shortcut key to jump to this function, my cursor is probably on the function name. In Vim, I'd do `cinr2:<esc>`. Seven keystrokes. I could also do `/[<cr><ctrl-a>` which is only four keystrokes, but a little more awkward to reach. If I tried to do this editing with arrow keys and backspaces I'd need 12 down keystrokes just to get to the right line.Or suppose I'm looking at this and want to add a `,` character to the end of each of the type entries, because I forgot Python likes its commas:
result = result | {
'a': TYPES_ALL
'e': TYPES_FILE_REAL
'x': TYPES_FILE_SYMLINK
'c': TYPES_DIR_REAL
'y': TYPES_DIR_SYMLINK
'f': TYPES_FILE
'd': TYPES_DIR
'r': TYPES_REAL
's': TYPES_SYMLINK
}[c.lower()]
In Vim I'd press `j` to get to the first entry, then `A,<esc>` to append the `,` after the first one, then `j.` to move down and repeat the action. I then press `j.` a bunch more times (which is really easy to do rapidly with your index and ring finger on Qwerty) and in 2 seconds I'm done. Vim's `.` to repeat the last action is really powerful and can save you a ton of time for small ad-hoc edit repetitions that don't warrant doing a full macro with `q`.These kinds of things happen constantly, and when you've got Vim burned into your fingers trying to edit text without it (actually edit text, not just jumping to something) feels like typing with oven mitts on.
Handling large arbitrary JSON objects where I need to operate on their keys but don't need to work with their values by unmarshaling to map[string]interface{}.
If you just need the keys, you should unmarshal to `map[string]json.RawMessage` to avoid the overhead of parsing the values into objects only to throw them away without ever using them.
And the fourth: scope creep.
For example the bell character, after which Bel is named, is \bel
The anonymous pipes are also called "process substitution", and are done with `psub` in fish:
sjl at alephnull in ~/scratch/tmp
><((°> diff -U0 (ls -1 | psub) (ls -1 | grep -v foo | psub)
--- /tmp/.psub.cIcXKeQj3O 2019-10-10 10:21:58.940444840 -0400
+++ /tmp/.psub.7W6mBnxcQD 2019-10-10 10:21:58.952444799 -0400
@@ -3 +2,0 @@
-fooYeah I considered trying some dynamic-extent declarations but just didn't care all that much. Can you post your version? I'm curious how far into the declaration weeds you need to go to get that extra 2x.
EDIT: I'm also curious how much using an optimized vector math library (e.g. sb-cga) would buy you instead of hand-rolling your own vector math. It would certainly be easier.
I looked over the Common Lisp version at https://github.com/niofis/raybench/blob/master/lisprb.lisp and it's… really bad, in a lot of ways.
(declaim (optimize (speed 3) (safety 0) (space 0) (debug 0) (compilation-speed 0)))
Never use `(optimize (safety 0))` in SBCL — it throws safety completely out the window. We're talking C-levels of safety at that point. Buffer overruns, the works. It might buy you 10-20% speed, but it's not worth it. Lisp responsibly, use `(safety 1)`. (defconstant WIDTH 1280)
People generally name constants in CL with +plus-muffs+. Naming them as uppercase doesn't help because the reader uppercases symbol names by default when it reads. So `(defconstant WIDTH ...)` means you can no longer have a variable named `width` (in the same package). (defstruct (vec
(:conc-name v-)
(:constructor v-new (x y z))
(:type (vector float)))
x y z)
Using `:type (vector float)` here is trying to make things faster, but failing. The type designator `float` covers all kinds of floats, e.g. both `single-float`s and `double-float`s in SBCL. So all SBCL knows is that the struct contains some kind of float, and it can't really do much with that information. This means all the vector math functions below have to fall back to generic arithmetic, which is extremely slow. SBCL even warns you about this when it's compiling, thanks to the `(optimize (speed 3))` declaration, but I guess they ignored or didn't understand those warnings. (defconstant ZERO (v-new 0.0 0.0 0.0))
This will cause problems because if it's ever evaluated more than once it'll try to redefine the constant to a new `vec` instance, which will not be `eql` to the old one. Use `alexandria:define-constant` or just make it a global variable.All the vector math functions are slow because they have no useful type information to work with:
(disassemble 'v-add)
; disassembly for V-ADD
; Size: 160 bytes. Origin: #x52D799AF
; 9AF: 488B45F8 MOV RAX, [RBP-8] ; no-arg-parsing entry point
; 9B3: 488B5001 MOV RDX, [RAX+1]
; 9B7: 488B45F0 MOV RAX, [RBP-16]
; 9BB: 488B7801 MOV RDI, [RAX+1]
; 9BF: FF1425A8001052 CALL QWORD PTR [#x521000A8] ; GENERIC-+
; 9C6: 488955E8 MOV [RBP-24], RDX
; 9CA: 488B45F8 MOV RAX, [RBP-8]
; 9CE: 488B5009 MOV RDX, [RAX+9]
; 9D2: 488B45F0 MOV RAX, [RBP-16]
; 9D6: 488B7809 MOV RDI, [RAX+9]
; 9DA: FF1425A8001052 CALL QWORD PTR [#x521000A8] ; GENERIC-+
; 9E1: 488BDA MOV RBX, RDX
; 9E4: 488B45F8 MOV RAX, [RBP-8]
; 9E8: 488B5011 MOV RDX, [RAX+17]
; 9EC: 488B45F0 MOV RAX, [RBP-16]
; 9F0: 488B7811 MOV RDI, [RAX+17]
; 9F4: 48895DE0 MOV [RBP-32], RBX
; 9F8: FF1425A8001052 CALL QWORD PTR [#x521000A8] ; GENERIC-+
; 9FF: 488B5DE0 MOV RBX, [RBP-32]
; A03: 49896D40 MOV [R13+64], RBP ; thread.pseudo-atomic-bits
; A07: 498B4520 MOV RAX, [R13+32] ; thread.alloc-region
; A0B: 4C8D5830 LEA R11, [RAX+48]
; A0F: 4D3B5D28 CMP R11, [R13+40]
; A13: 772E JNBE L2
; A15: 4D895D20 MOV [R13+32], R11 ; thread.alloc-region
; A19: L0: C600D9 MOV BYTE PTR [RAX], -39
; A1C: C6400806 MOV BYTE PTR [RAX+8], 6
; A20: 0C0F OR AL, 15
; A22: 49316D40 XOR [R13+64], RBP ; thread.pseudo-atomic-bits
; A26: 7402 JEQ L1
; A28: CC09 BREAK 9 ; pending interrupt trap
; A2A: L1: 488B4DE8 MOV RCX, [RBP-24]
; A2E: 48894801 MOV [RAX+1], RCX
; A32: 48895809 MOV [RAX+9], RBX
; A36: 48895011 MOV [RAX+17], RDX
; A3A: 488BD0 MOV RDX, RAX
; A3D: 488BE5 MOV RSP, RBP
; A40: F8 CLC
; A41: 5D POP RBP
; A42: C3 RET
; A43: L2: 6A30 PUSH 48
; A45: FF142520001052 CALL QWORD PTR [#x52100020] ; ALLOC-TRAMP
; A4C: 58 POP RAX
; A4D: EBCA JMP L0
If they had done the type declarations correctly, it would look more like this: ; disassembly for V-ADD
; Size: 122 bytes. Origin: #x52C33A78
; 78: F30F104A05 MOVSS XMM1, [RDX+5] ; no-arg-parsing entry point
; 7D: F30F105F05 MOVSS XMM3, [RDI+5]
; 82: F30F58D9 ADDSS XMM3, XMM1
; 86: F30F104A0D MOVSS XMM1, [RDX+13]
; 8B: F30F10670D MOVSS XMM4, [RDI+13]
; 90: F30F58E1 ADDSS XMM4, XMM1
; 94: F30F104A15 MOVSS XMM1, [RDX+21]
; 99: F30F105715 MOVSS XMM2, [RDI+21]
; 9E: F30F58D1 ADDSS XMM2, XMM1
; A2: 49896D40 MOV [R13+64], RBP ; thread.pseudo-atomic-bits
; A6: 498B4520 MOV RAX, [R13+32] ; thread.alloc-region
; AA: 4C8D5820 LEA R11, [RAX+32]
; AE: 4D3B5D28 CMP R11, [R13+40]
; B2: 7734 JNBE L2
; B4: 4D895D20 MOV [R13+32], R11 ; thread.alloc-region
; B8: L0: 66C7005903 MOV WORD PTR [RAX], 857
; BD: 0C03 OR AL, 3
; BF: 49316D40 XOR [R13+64], RBP ; thread.pseudo-atomic-bits
; C3: 7402 JEQ L1
; C5: CC09 BREAK 9 ; pending interrupt trap
; C7: L1: C7400103024F50 MOV DWORD PTR [RAX+1], #x504F0203 ; #<SB-KERNEL:LAYOUT for VEC {504F0203}>
; CE: F30F115805 MOVSS [RAX+5], XMM3
; D3: F30F11600D MOVSS [RAX+13], XMM4
; D8: F30F115015 MOVSS [RAX+21], XMM2
; DD: 488BD0 MOV RDX, RAX
; E0: 488BE5 MOV RSP, RBP
; E3: F8 CLC
; E4: 5D POP RBP
; E5: C3 RET
; E6: CC0F BREAK 15 ; Invalid argument count trap
; E8: L2: 6A20 PUSH 32
; EA: E8F1C64CFF CALL #x521001E0 ; ALLOC-TRAMP
; EF: 58 POP RAX
; F0: EBC6 JMP L0
The weirdness continues: (defstruct (ray
(:conc-name ray-)
(:constructor ray-new (origin direction))
(:type vector))
origin direction)
The `:conc-name ray-` is useless, that's the default conc-name. And again with the `:type vector`… just make it a normal struct. I was going to guess that they were doing it so they could use vector literals to specify the objects, but then why are they bothering to define a BOA constructor here? And the slots are untyped, which, if you're looking for speed, is not doing you any favors.I took a few minutes over lunch to add some type declarations to the slots and important functions, inlined the math, cleaned up the broken indentation and naming issues:
https://gist.github.com/sjl/005f27274adacd12ea2fc7f0b7200b80...
The old version runs in 5m12s on my laptop, the new version runs in 58s. So if we unscientifically extrapolate that to their 24m time, it puts it somewhere around 5m in their list. This matches what I usually see from SBCL: for numeric-heavy code generic arithmetic is very slow, and some judicious use of type declarations can get you to within ~5-10x of C. Getting more improvements beyond that can require really bonkers stuff that often isn't worth it.