I found this code challenge on checkio.org, but the really interesting thing to me are the solutions which show unique and educational approaches.
The code challenge was this:
You are given a text which only contains ASCII symbols.
(a)You should find the most frequent letter in the text. The letter returned must be in lower case.
Ignore all other characters such as digits, punctuation and whitespace.
(b)If you have two or more letters with the same highest frequency,
then return the letter which comes first in the latin alphabet.
For example -- "one" contains "o", "n", "e" only once for each, thus we choose "e".
input: text, output: most frequent letter in text
My solution, and many others, took about 10-12 lines of code. Checkio user veky’s solution uses only two!
Here’s his solution:
from string import ascii_lowercase as letters
checkio = lambda text: max(letters, key=text.lower().count)
The checkio webpage providing the solutions is only available to users who have solved the challenge, so I can’t simply give you the url. I decided veky’s explanation is worth posting here.
When asked to explain his solution and the difference between bound and unbound methods, veky had this to say (in response to a question):
Np. Glad you learned something. BTW the concept of bound methods is much more important lesson here than lambda. ;-) [Especially since it can be used to avoid an unnecessary lambda.;]
First, when trying to grasp language concepts, please refer to Py3 documentation, not Py2. The documentation is clearer, but more importantly, the language itself is clearer and has fewer basic concepts. It’s always better to learn something new on Py3, and then only if you’re curious or you need to, see what are the differences on Py2.
Case in point: Py3 has only one kind of classes, and bound methods work the same for all of them. Py2 has two kinds, “classic” and “modern” classes, which are irreducible one to another, and have slightly different notions of bound methods. Also, Py2 has the notion of unbound method as a separate object - in Py3, “unbound method” is just an ordinary function. And so on.
Since you want me to use (natural) language rather than code, I’ll use circumlocution to answer your question. :-P In my code, neither the
lambda
(calledcheckio
) normax
are bound methods. Bound methods are what you get when you access the attribute on an instance, which on the class is an ordinary function. So, ifA.f
is a function, andisinstance(a, A)
, (usually)a.f
is a bound method.
What’s the difference between
A.f
anda.f
? They are both callable, but they (usually) differ in the number of arguments they are callable with. IfA.f
is a function accepting three arguments (self
,x
andy
), thena.f
is callable with two arguments, namedx
andy
. In the body off
, the nameself
refers toa
itself. So, we can saya.f(3, 5) <~~~> A.f(a, 3, 5)
This has many benefits, but the one I use here is cheap specialization. That
f
above probably does something withself
, withx
and withy
, but there are many cases when what it does withself
is common to many invocations. In other words, it’s called many times with the sameself
, whilex
andy
vary.
In this concrete example, the
key
parameter tomax
is a function. It counts how many times a letter appears in text (lowered, but let’s ignore that for now). We can write
str.count(text, 'a'), str.count(text, 'b'), str.count(text, 'c'), ...
but in fact all of these use the same first argument, while second argument is the one that key function is expecting. We can write
, key=lambda x: str.count(text, letter)
but in fact bound methods give us a much easier way. Since str.count(text, letter)
is the same as text.count(letter)
, we have a perfectly fine callable thing to set key
to. Simply
key=text.count
Clearer?