Grasping `all-the-apples-at-once' › 解法样本
Sample solutions
[jsr-000W]
Grasping `all-the-apples-at-once' › 解法样本 Sample solutions [jsr-000W]
提交到 Lobsters 供讨论的博客文章描述了在 Clojure 中的一个解决方案。但不知为何,这促使会员们用自己选择的编程语言来解决这个问题并发布结果。于是这就形成了一场即兴竞赛。
The blog post submitted to Lobsters for discussion described one solution, in Clojure. Somehow it prompted the members to solve the problem in their language of choice, and post the results. Hence there developed an impromptu contest.
以下是已发布的解法样本。这些解决方案由不同的人用不同的编程语言编写⸺然而不知为何,它们分成了两组,我暂时称之为 Group I 和 Group II。我稍后会告诉你这些组的意义⸺请让我卖个关子。
Below is a sample of posted solutions. They are written in various languages by various people -- yet somehow fall into two groups, which I call for now Group I and Group II. I'll tell what they mean only later -- I hope you'd allow me a bit of suspense.
1. Clojure (Group I) [#277]
1. Clojure (Group I) [#277]
(->> "aaaabbbcca"
(partition-by identity)
(map (juxt (comp str first) count)))
提交者还提供了另一个解法:“有点繁琐,但更清晰”:
(for [p (partition-by identity "aaaabbbcca" )]
[(-> p first str) (count p)])
The submitter also gave another solution, ``a bit chattier but also more legible:''
值得注意的是,
comp
是
函数组合
,而 juxt
粗略的类型是 (a -> b * a -> c) -> a -> (b * c)
,这是一个组合子,它接收一个函数二元组和一个值,对该值应用两个函数,然后返回由这两个函数的返回值组成的元组。
It is worth noting that comp is the functional composition, and juxt, with the rough type (a->b * a->c) -> a -> (b * c), is the combinator that takes a tuple of functions and a value, applies each function to it and returns the results as a tuple.
2. Ruby (Group I) [#278]
2. Ruby (Group I) [#278]
Ruby 有三种解法⸺都是单行的,而且很相似。第一个可能是最容易理解的。
There were three solutions in Ruby -- all one-liners, and rather similar. The first is probably the easiest to understand.
"aaaabbbcca".split("").chunk_while {|a,b| a==b}.map {|a| [a.first, a.size]}
"aaaabbbcca".chars.slice_when {|c,n| c != n }.map {|s| [s[0], s.size]}
'aaaabbbcca'.chars.chunk(&:itself).map{|x,y| [x, y.size]}
3. APL (Group I) [#279]
3. APL (Group I) [#279]
两个 APL 解法:
Two APL solutions:
(((1∘+ +\) ⊢ ≠ ¯1∘⌽) (((⊃,≢) ⊢) ⌸) ⊢) 'aaaabbbcca'
f←(⊃,≢)¨⊢⊆⍨1+(+\0,2≠/⊢) ⊢x←f'aaaabbbcca'
4. GO (Group II) [#280]
4. GO (Group II) [#280]
我知道这是一个 Clojure 的帖子,但是出于好奇,我用 Go 编写了它(我最近一直在使用 Go)。只是为了看看是否可以快速解决。我花了大约 10 分钟,减去 3-4 分钟与 runes 战斗的时间。 这不是特别优雅,但能工作。
``I know this is a Clojure post, but out of curiosity I wrote it in Go (what I've been working in lately) just to see if I could solve it quickly. Took about 10 minutes, subtract 3-4 for fighting with runes. It's not particularly elegant, but it works.''
type result struct {
letter string
count int
}
func main() {
const input = "aaaabbbcca"
var ret []result
currentLetter := string(input[0])
countCurrentLetter := 1
for _, elem := range input[1:] {
elemAsString := string(elem)
if currentLetter == elemAsString {
countCurrentLetter++
} else {
ret = append(ret, result{currentLetter, countCurrentLetter})
currentLetter = elemAsString
countCurrentLetter = 1
}
}
ret = append(ret, result{currentLetter, countCurrentLetter})
fmt.Printf("%+v", ret)
}
该解法与类似解法的显著特征是明确的迭代:
for
循环。
The salient feature of this and similar solutions is explicit iteration: the for loop.
5. Racket (Group II) [#281]
5. Racket (Group II) [#281]
(for/fold ([p #f]
[cnt 0]
[counts null]
#:result (cdr (reverse (cons `(,p ,cnt) counts))))
([c (in-string "aaaabbbcca")])
(if (equal? p c)
(values c (add1 cnt) counts)
(values c 1 (cons `(,p ,cnt) counts))))
该解法是纯函数式的⸺但与上面 GO 的解决方案非常相似。
The solution is pure functional -- yet closely resembling the Go solution above.
6. Raku (Group I) [#282]
6. Raku (Group I) [#282]
"aaaabbbcca".subst( /(.) $0*/ , {"({$0},{$/.chars})" }, :g )
Raku 是前身为 Perl 6 的艺术品。Perl 一直以其(非常)正则表达式而闻名,威力可见一斑。
Raku is the artist formerly known as Perl 6. Perl has always been known for its (ir)regular expressions, whose power is on display.
7. Erlang (Group II) [#283]
7. Erlang (Group II) [#283]
L = "aaaabbbcca",
lists:reverse(lists:foldl(
fun
(Elt, [ {[Elt], N} | R ]) -> [ {[Elt], N + 1} | R];
(Elt, Acc) -> [{[Elt], 1} | Acc]
end,
[],
L
)).
与 Racket 解法相似。我们看到了 Erlang
非线性模式匹配
的特征:在第一个模式中变量
Elt
出现了两次。
It is rather similar to the Racket solution. We see the characteristic for Erlang non-linear pattern matching: the variable Elt appears twice in the first pattern.
8. Python (Group I) [#284]
8. Python (Group I) [#284]
[(k, len(list(g))) for k, g in itertools.groupby("aaaabbbcca")]
9. Rust (Group II) [#285]
9. Rust (Group II) [#285]
fn run_length_encode(ins: &str) -> Vec<(char, usize)> {
let mut out = vec![];
let mut i = ins.chars();
if let Some(mut c) = i.next() {
let mut count = 1;
for new_c in i {
if new_c == c {
count += 1;
} else {
out.push((c, count));
count = 1;
c = new_c;
}
}
out.push((c, count));
}
out}
就像 GO 的解法一样⸺但是更干净,并且更好地处理边缘情况。作者确实掌握了问题的本质:
游程编码
。
It is quite like the solution in Go -- but cleaner and with a better handling of edge cases. The author did grasp the nature of the problem: run-length encoding.
10. Julia (Group II) [#286]
10. Julia (Group II) [#286]
function rle(itr)
acc = Tuple{eltype(itr), Int}[]
x = iterate(itr)
isnothing(x) && return acc # iterable is empty
last, state = x
n = 1
for v in Iterators.rest(itr, state)
if last == v
n += 1
else
push!(acc, (last, n))
last = v
n = 1
end
end
push!(acc, (last, n))
return acc
end
镜像了上面的 Rust 解法。
It mirrors the Rust solution above.
11. Haskell (Group II) [#287]
11. Haskell (Group II) [#287]
main = interact soln
soln = show . reverse . go []
go counts [] = counts
go counts (x:xs) = go (inc x counts) xs
inc c ((x,ct):xs) | c == x = (x,ct+1):xs
inc c xs = (c, 1):xs
尽管该解法依赖于显式递归而不是
fold
,但它与 Erlang 解法类似。(后来有另一个人提到了另一种使用 group
的解法)。
Although this solution relies on the explicit recursion rather than fold, it is rather similar to the Erlang solution. (A different person later mentioned another solution, using group).