[Snark] F# No Whitespace

Christopher Vollick 0 at psycoti.ca
Sat Nov 17 18:16:53 UTC 2018

On Wed, Oct 24, 2018 at 07:15:26PM -0500, Phillip Lane wrote:
> for i in str.Split(' ') do
>    let word =
>        if i.Length > 2
>           && Array.exists
>                  (fun x ->
>                  x = System.Convert.ToString(i.[0])
>                      + System.Convert.ToString(i.[1])
>                      + System.Convert.ToString(i.[2])) consonants then
>            i.Substring 3 + System.Convert.ToString(i.[0])
>            + System.Convert.ToString(i.[1]) + System.Convert.ToString(i.[2])
>            + "ay"

Alright, this is the rough pattern with this one.
We bounds-check our word, called `i`, and then we use a repeated application of indexing, converting to strings, and concatting to build up our prefix.
Then we check through all of our consonants to see if we're in a special case, and handle it by using the actual Substring method (which can make prefixes) and catting the first few characters back on.

I can get behind that.

Also, I don't know enough F# to know for sure, but I'm pretty sure that at each iteration of the `exists` function (so for each consonant) it will _again_ index, convert, concat.
That's a nice touch, I think, since it'll produce the same result each time, which could be captured and given a name if this was somehow the only way to build this string.
Which it's not.

Also, I do kinda like that `consonants` here isn't just a list of all consonants like you might expect, since it also contains the special cases like "th" and "qu".
I like that if someone missed that first definition and was just looking at the code they might think there were special-cases that were missed.

>        else if i.Length > 4
>                && Array.exists
>                       (fun x ->
>                       x = System.Convert.ToString(i.[0])
>                           + System.Convert.ToString(i.[1])
>                           + System.Convert.ToString(i.[2])
>                           + System.Convert.ToString(i.[3]))
>                       (Array.map (fun x -> x + "qu") consonants) then
>            i.Substring 4 + System.Convert.ToString(i.[0])
>            + System.Convert.ToString(i.[1]) + "quay"

"Oh shoot, sometimes squ is a problem too! What should we do?"
"It's ok. We'll just cut and paste the previous case, and jam qu on the back of everything"

I also like that some of the things in consonants are 3 characters long themselves, so "schqu" would actually only be compared to "schq", but that's fine, because we no one ever uses that!
Tests pass!
I like to imagine that this case was created before the 3 letter variants were added to consonants, and no one bothered to update it.

That being said... the 4-letter combos would be "chqu", "phqu", "ququ", "shqu", "thqu", and "rhqu", and I don't think they come up either...
It's possible this case is actually never used. And I'm ok with that.

>        else if i.Length > 3
>                && Array.exists
>                       (fun x ->
>                       x = System.Convert.ToString(i.[0])
>                           + System.Convert.ToString(i.[1])
>                           + System.Convert.ToString(i.[2]))
>                       (Array.map (fun x -> x + "qu") consonants) then
>            i.Substring 3 + System.Convert.ToString(i.[0]) + "quay"

Another good time just copy-and-pasting the above and tweaking it a little.
By now we've put "qu" on the back of every consonant twice just in case.

>    if i = str.Split(' ').[str.Split(' ').Length - 1] then printf "%s" word
>    else printf "%s " word

Ooh, this is a nice touch.
So, we want to join the words together with spaces, obviously.
But there's no space after the final word!
So we need to figure out on _this_ word if this one is the last one.

So we take the original string and split it again, then we take the original string and split it _again_ to find how many words there are, then subract 1 to get the last word from that last time we split it.
Then, if _this_ word is the same as the last word, it doesn't put a space, otherwise it does.

While the duplication and inefficiency is nice, there's also a bug lurking in here.

If our input was "barn fox bite fox", I think this would produce "arnbay oxfayitebay oxfay"
But no tests failed, so it went undetected.
This is exactly the kind of oversight bad code allows!
Good times.

All in all, the solution to the problem was _relatively_ straight-forward, but the implementation was just annoying enough to believe someone who was armed with only StackOverflow, copy, and paste could have struggled their way through it to produce this.

If they searched "how do I take the first character off a string", and then extrapolated that out to get other characters, and then searched "how do I make a character into a string", and "how do I join two strings", instead of "how do I make a substring", this is exactly the kind of solution I'd expect.
And then each case is just a simple edit of the previous. Easy peasy!

Not too bad!

More information about the Snark mailing list