Post History
Python 3, 120 116 bytes a=b"F99%T / 9#mQ 9 %>^/%O/#*c 4bJLlHIgwY[NXrEdSsheZ]"*3 def f(l):l.sort(key=lambda s:a.find(len(s)+a[s[0]]+a[s[1]])) Takes input as a list of bytes, representing low...
#2: Post edited
# Python 3, 120 bytes- a=b"F99%T / 9#mQ 9 %>^/%O/#*c 4bJLlHIgwY[NXrEdSsheZ]"*3
f=lambda l:sorted(l,key=lambda s:a.find(len(s)+a[s[0]]+a[s[1]]))Takes input as a list of `bytes`, representing lower-case colour names encoded in UTF-8 (or ASCII). [Try it online!](https://tio.run/##dVHBSsNAEL33K0JKIa3FKvWSQr0pKqKIHqohwmQzaZZOdsNs0hB/via7rS2Kp5l58@btm9myrXKt5rsdLBP/NgxHb97MC4fFixd6o@vP2eh5NpwI7yp5eKS7@3XzHj2t@CZ9NTl@xP5kPsiWBEWSgkcLo7nCNKDpBtsDahZwnknVoagCMz6DyEQXcWzjZRyPx7uSpaqCLIi6YvBTJP6aEZU/TfyEauwjY2pL1o3yf5M12WY31O4j/iFxndhmi0S6OZG0U/8rOhsZNDY2uaysH/iq2RnTBp1REJs@KWsuyUKaQa1tZiRtkS19b@OwF8lurk8Ey8Jo@0gp1cZBCIUlYeE6XBuD1emumuTWCm2lJtcqoHaQyLXQBM7wce8SQeTHY/YiInfLGAG8V@mv0//QNw "Python 3 – Try It Online")- ## Explanation
- I ran the list through `gperf` version 3.1, which found the following perfect hash function:
- asso_values = {
- 'a' => 25, 'b' => 25, 'c' => 5, 'e' => 0,
- 'f' => 15, 'g' => 0, 'h' => 25, 'i' => 3,
- 'l' => 0, 'm' => 25, 'o' => 5, 'p' => 30,
- 'r' => 15, 's' => 5, 'u' => 15, 'v' => 3,
- 'w' => 10, 'y' => 0, 'z' => 20,
- }
- fn hash(s: str) -> int {
- len(s) + asso_values[s[1]] + asso_values[s[0]]
- }
- wordlist = {
- [ 5] = "lemon", "yellow",
- [ 8] = "lilac", "gold", "olive",
- [12] = "violet",
- [14] = "silver", "ochre",
- [17] = "scarlet", "red", "grey", "green",
- [24] = "rose", "cream", "orange", "crimson",
- [29] = "blue", "black",
- [34] = "ruby", "peach", "russet", "pink",
- [39] = "chocolate", "white",
- [44] = "fawn", "brown",
- [50] = "azure", "purple",
- [55] = "mauve",
- }
- This hash function still works if we give everything a constant offset. I picked `64`, which is twice the codepoint of the Unicode SPACE character. `asso_values` does not contain `2`, so this allows us to represent it as a bytestring (where `.` represents an unused value):
- a = b'.'*97 + b"99%. / 9#.. 9 %>./%./#*. 4"
- Then – using `bytes` as input, so we don't need to call `ord` –, the hash function becomes:
- def hash(s: bytes) -> int:
- return len(s) + a[s[1]] + a[s[0]]
- Now, we just need to map each hash key to its position in the song. We could subtract 69 (the smallest hash key, = 5 + 64), and then look them up in another sparse array; however, since we only care about the _order_ (they do not need to map to particular values), we can store them more compactly as a string:
- b"RFTmQ^OcbJLlHIgwY[NXrEdSsheZ]"
- (Serendipitously, "red" maps to `R`, and "ochre" maps to `O`.)
- Now, to find the relative position of each word, we can index the string with `str.find`: the worse-but-shorter version of `str.index`, which returns `-1` when it can't find the value. Since `-1 < 0`, we can actually remove `R` from the string entirely!
- We could store this as a separate constant, but – again – we only care about the _order_. And we've got a lot of unused space in our existing string… Interleaving them, we can get the string down to 48 characters, repeated 3 times.
The rest of the code is a straightforward call to `sorted`, using anonymous functions (`lambda`s) for brevity. I'm annoyed by how verbose this part is, but I don't see a way to cut it down.- ---
- I'm sure there's plenty of room for golfing, starting with a better perfect hash function: choosing different indices may allow us to eliminate the `len` dependency, and adding a subtraction or a constant offset may allow us to _share_ characters between the hash table and the key order (rather than just interleaving the data). For now, though, I'm satisfied.
- # Python 3, <del>120</del> <ins>116</ins> bytes
- a=b"F99%T / 9#mQ 9 %>^/%O/#*c 4bJLlHIgwY[NXrEdSsheZ]"*3
- def f(l):l.sort(key=lambda s:a.find(len(s)+a[s[0]]+a[s[1]]))
- Takes input as a `list` of `bytes`, representing lower-case colour names encoded in UTF-8 (or ASCII). Mutates the list by sorting it in-place. [Try it online!](https://tio.run/##bVDLTsMwELz3K6xUlZKCWhBcUlRuIEAIhOAAREaynU1idWNHdtIo/Hyo7UJ74OJZz87OPpqhrbS6GEe25tFtms7eyJKk0/qFpGR2/bWcPS@nc0Eu@cMj3t2X/Uf29G5u8ldbwSeN5heTHApSxJiscGG1aeMNDGtkNc8ZsSu2KKTKYwQV2@SEZTY7o9TjOaVJMrri0hV7iyvSGKnaXTSZlHFGE/fyqDQAKjrlEccOHBrI/dfoXkV/Ko2e3amHPcIhazru2QEQdX9k4uX/eISOBes99pVsfWv23Zkwg7YQZmJi44KmMw16ShumSh9ZiVswXr7v/7sCyl2dC4SRtdW@SSPVJlDAai@COmRMZy20x9tplFtvtJUaQ6pmXaBEpYVGFgY@LNwAE9Xhbs5EVGEZK5jZu7iz0GT8AQ "Python 3 – Try It Online")
- ## Explanation
- I ran the list through `gperf` version 3.1, which found the following perfect hash function:
- asso_values = {
- 'a' => 25, 'b' => 25, 'c' => 5, 'e' => 0,
- 'f' => 15, 'g' => 0, 'h' => 25, 'i' => 3,
- 'l' => 0, 'm' => 25, 'o' => 5, 'p' => 30,
- 'r' => 15, 's' => 5, 'u' => 15, 'v' => 3,
- 'w' => 10, 'y' => 0, 'z' => 20,
- }
- fn hash(s: str) -> int {
- len(s) + asso_values[s[1]] + asso_values[s[0]]
- }
- wordlist = {
- [ 5] = "lemon", "yellow",
- [ 8] = "lilac", "gold", "olive",
- [12] = "violet",
- [14] = "silver", "ochre",
- [17] = "scarlet", "red", "grey", "green",
- [24] = "rose", "cream", "orange", "crimson",
- [29] = "blue", "black",
- [34] = "ruby", "peach", "russet", "pink",
- [39] = "chocolate", "white",
- [44] = "fawn", "brown",
- [50] = "azure", "purple",
- [55] = "mauve",
- }
- This hash function still works if we give everything a constant offset. I picked `64`, which is twice the codepoint of the Unicode SPACE character. `asso_values` does not contain `2`, so this allows us to represent it as a bytestring (where `.` represents an unused value):
- a = b'.'*97 + b"99%. / 9#.. 9 %>./%./#*. 4"
- Then – using `bytes` as input, so we don't need to call `ord` –, the hash function becomes:
- def hash(s: bytes) -> int:
- return len(s) + a[s[1]] + a[s[0]]
- Now, we just need to map each hash key to its position in the song. We could subtract 69 (the smallest hash key, = 5 + 64), and then look them up in another sparse array; however, since we only care about the _order_ (they do not need to map to particular values), we can store them more compactly as a string:
- b"RFTmQ^OcbJLlHIgwY[NXrEdSsheZ]"
- (Serendipitously, "red" maps to `R`, and "ochre" maps to `O`.)
- Now, to find the relative position of each word, we can index the string with `str.find`: the worse-but-shorter version of `str.index`, which returns `-1` when it can't find the value. Since `-1 < 0`, we can actually remove `R` from the string entirely!
- We could store this as a separate constant, but – again – we only care about the _order_. And we've got a lot of unused space in our existing string… Interleaving them, we can get the string down to 48 characters, repeated 3 times.
- The rest of the code is a straightforward call to `list.sort` using an anonymous function (`lambda`). I'm annoyed by how verbose this part is, but I don't see a way to cut it down.
- ---
- I'm sure there's plenty of room for golfing, starting with a better perfect hash function: choosing different indices may allow us to eliminate the `len` dependency, and adding a subtraction or a constant offset may allow us to _share_ characters between the hash table and the key order (rather than just interleaving the data). For now, though, I'm satisfied.
#1: Initial revision
# Python 3, 120 bytes
a=b"F99%T / 9#mQ 9 %>^/%O/#*c 4bJLlHIgwY[NXrEdSsheZ]"*3
f=lambda l:sorted(l,key=lambda s:a.find(len(s)+a[s[0]]+a[s[1]]))
Takes input as a list of `bytes`, representing lower-case colour names encoded in UTF-8 (or ASCII). [Try it online!](https://tio.run/##dVHBSsNAEL33K0JKIa3FKvWSQr0pKqKIHqohwmQzaZZOdsNs0hB/via7rS2Kp5l58@btm9myrXKt5rsdLBP/NgxHb97MC4fFixd6o@vP2eh5NpwI7yp5eKS7@3XzHj2t@CZ9NTl@xP5kPsiWBEWSgkcLo7nCNKDpBtsDahZwnknVoagCMz6DyEQXcWzjZRyPx7uSpaqCLIi6YvBTJP6aEZU/TfyEauwjY2pL1o3yf5M12WY31O4j/iFxndhmi0S6OZG0U/8rOhsZNDY2uaysH/iq2RnTBp1REJs@KWsuyUKaQa1tZiRtkS19b@OwF8lurk8Ey8Jo@0gp1cZBCIUlYeE6XBuD1emumuTWCm2lJtcqoHaQyLXQBM7wce8SQeTHY/YiInfLGAG8V@mv0//QNw "Python 3 – Try It Online")
## Explanation
I ran the list through `gperf` version 3.1, which found the following perfect hash function:
asso_values = {
'a' => 25, 'b' => 25, 'c' => 5, 'e' => 0,
'f' => 15, 'g' => 0, 'h' => 25, 'i' => 3,
'l' => 0, 'm' => 25, 'o' => 5, 'p' => 30,
'r' => 15, 's' => 5, 'u' => 15, 'v' => 3,
'w' => 10, 'y' => 0, 'z' => 20,
}
fn hash(s: str) -> int {
len(s) + asso_values[s[1]] + asso_values[s[0]]
}
wordlist = {
[ 5] = "lemon", "yellow",
[ 8] = "lilac", "gold", "olive",
[12] = "violet",
[14] = "silver", "ochre",
[17] = "scarlet", "red", "grey", "green",
[24] = "rose", "cream", "orange", "crimson",
[29] = "blue", "black",
[34] = "ruby", "peach", "russet", "pink",
[39] = "chocolate", "white",
[44] = "fawn", "brown",
[50] = "azure", "purple",
[55] = "mauve",
}
This hash function still works if we give everything a constant offset. I picked `64`, which is twice the codepoint of the Unicode SPACE character. `asso_values` does not contain `2`, so this allows us to represent it as a bytestring (where `.` represents an unused value):
a = b'.'*97 + b"99%. / 9#.. 9 %>./%./#*. 4"
Then – using `bytes` as input, so we don't need to call `ord` –, the hash function becomes:
def hash(s: bytes) -> int:
return len(s) + a[s[1]] + a[s[0]]
Now, we just need to map each hash key to its position in the song. We could subtract 69 (the smallest hash key, = 5 + 64), and then look them up in another sparse array; however, since we only care about the _order_ (they do not need to map to particular values), we can store them more compactly as a string:
b"RFTmQ^OcbJLlHIgwY[NXrEdSsheZ]"
(Serendipitously, "red" maps to `R`, and "ochre" maps to `O`.)
Now, to find the relative position of each word, we can index the string with `str.find`: the worse-but-shorter version of `str.index`, which returns `-1` when it can't find the value. Since `-1 < 0`, we can actually remove `R` from the string entirely!
We could store this as a separate constant, but – again – we only care about the _order_. And we've got a lot of unused space in our existing string… Interleaving them, we can get the string down to 48 characters, repeated 3 times.
The rest of the code is a straightforward call to `sorted`, using anonymous functions (`lambda`s) for brevity. I'm annoyed by how verbose this part is, but I don't see a way to cut it down.
---
I'm sure there's plenty of room for golfing, starting with a better perfect hash function: choosing different indices may allow us to eliminate the `len` dependency, and adding a subtraction or a constant offset may allow us to _share_ characters between the hash table and the key order (rather than just interleaving the data). For now, though, I'm satisfied.
