5 ms·
strong :: (Integral i) => [i] -> [i] strong = mapMaybe fst3 . scanr g (Nothing, 0, 0) where fst3 (x, _, _) = x g n (_, p, pp) = (p <$
by moomin 7y ago
strong :: (Integral i) => [i] -> [i]
strong = mapMaybe fst3 . scanr g (Nothing, 0, 0)
where fst3 (x, _, _) = x
g n (_, p, pp) = (p <$ guard (2 * p > pp + n), n, p)
Haskell makes a better Perl (especially if, like here, you don't bother with readability).
- oarabbus_ 7y agoWow, Haskell is just as opaque as ever to me. I wonder if I'll ever be able to comprehend this language.
- nixpulvis 7y agoJust go slowly, you can do it, just not as naturally as Ruby for example.
- jkeuhlen 7y agoHave you tried working through a more structured learning process like Haskelbook? Once you get the basics, the types begin to add lots of clarity to what is going on in a function and make it easier and faster to comprehend complicated behavior.
- andolanra 7y agoFor what it's worth, I've worked as a professional Haskell programmer at multiple companies and have been writing Haskell for more than a decade, and I still have trouble reading code written in that style. I usually end up decomposing it at a REPL so I can look at the types of intermediate parts of the program. (In practice, most—although not all, admittedly—of the Haskell codebases I've worked with tended to be a lot less dense than the occasional combinator-heavy sample code you see in blog posts and comments.)
- moomin 7y agoIn fairness to Haskell, I didn't exactly go for readability in the above example, and indeed someone's already added a more readable example. I'll break it down for the sake of completeness: scanr is a reduce function that produces a list of the reduction steps rather than just the final output. So I'm processing the list of primes and punting out (Maybe i, i i) at each stage (I could have included the type signature for "g" but I was deliberately going for a perl-like style.) fst3 just takes the first element of a triple i.e. Maybe i. mapMaybe maps and throws away the "Nothing"s. Think of those as nulls. So, returning to g, it returns a (Maybe i, i, i). The Maybe i is the prime if it's strong, Nothing if it isn't. The other two are the previous two primes. So what g does is a) compute the first element and b) copy the new prime (n) into the first location and the previous prime (p) into the second location. "<$ guard" is a bit of a Stupid Dwarf Trick in all honesty, but the effect is: the expression on the right is a boolean expression. If it returns true, the expression on the left is returned. If it returns false, "Nothing" is returned. If you are interested, I highly recommend working through this: https://www.cis.upenn.edu/~cis194/spring13/lectures.html https://www.cis.upenn.edu/~cis194/spring13/lectures.html (and that means doing the exercises). Haskell's a funny language in that there's a fairly easy way of doing things that would read how you would expect, and a whole bunch of "oh, but this is more elegant" things you can add on top. Thankfully, some of the most cutting edge features like ApplyingVia are actually producing _more_ readable code, not less.
- jkeuhlen 7y agoYou can also swap out that fst3 for a lens! (^. _3) works for any size tuple.
- CountHackulus 7y agoYou could also express it as a query over a comonadic stream.
- TremendousJudge 7y agoWhat is it with functional languages and people naming their variables with single letters? If I wrote some python code like that I'd be laughed out of the room
- Zhyl 7y agoBecause many of them, especially Haskell, have their roots in Mathematics which are famous for one letter variable names. Because the syntax favours terseness for readabilities sake (to see structure and patterns rather than reading a description of the variable). Because Python specifically favours verboseness and has many principles around using long names, underscores instead of camelcase and the like.
- pault 7y agoFine, but that doesn't address the point GP is making. This is a subject near and dear to my heart, since I am constantly frustrated by the opaque nature of functional programming tutorials on the internet. Functional composition is very difficult for beginners to pick up, and using single letter variable names for everything significantly compounds that problem. There are many concepts in FP that can elevate the craft of software design, and it's a tragedy that a lack of approachable instructional materials convinces most programmers that either they are not smart enough to learn it, or that it's Ivory tower nonsense. Ease of readability is a virtue in any programming environment, and I personally feel that the FP community as a whole should make lowering the barriers to entry a top priority. I'm grateful to all of the people working hard to bring functional programming to the masses, but I have a sneaking suspicion that many functional programming practitioners like that it looks complex and difficult to outsiders. Just my two cents.
- crazygringo 7y agoEveryone I know who programs with single letters comes from a graduate-level math background. There seems to be a kind of belief that elegance and compactness are desirable. Of course it’s the polar opposite of self-documenting, legible, understandable, easy-to-maintain code.
- ckw 7y agoGolf: strong :: (Integral i) => [i] -> [i] strong λ@(_:b:c) = view _2 <$> filter p (zip3 λ (b:c) c) where p (x, y, z) = 2 * y > x + z Also, it hangs with scanr (scanl works) and includes 2 which isn't strong.
- piinbinary 7y agoJust because I can't resist writing little math things in Haskell: primes = 2 : filter isPrime [3..] isPrime n = let divisors = takeWhile (\d -> d * d <= n) primes in not $ any (\d -> n `mod` d == 0) divisors strongPrimes = let primeTriples = zip3 primes (drop 1 primes) (drop 2 primes) in [b | (a, b, c) <- primeTriples, b*2 > a + c]
- kdmccormick 7y agoProof that you can write something elegant in Haskell and use comprehensible variable names.
- moomin 7y agoI like the zip3, I should have thought of that. I think I prefer the mapMaybe, but then I avoid list comprehensions.