I’ve seen a number of special computer keyboards. These include, for example, five-finger units that require learning special ‘chords’. The idea of a one-handed keyboard is enticing but I don’t like the sound of having to learn specific combinations. What about re-using existing knowledge?
Anyone who touch-types knows that each letter belongs to a given finger. I wondered what would happen if you restricted the keyboard to just one row (i.e. one button for each finger) whether this would make a functioning concept.
Touch-typists learn the ‘home row’. The left-hand little finger is responsible for Q, A and Z, and the right hand middle finger is responsible for I K and ,. The index fingers have to cover two columns, with the left hand doing R, F, V, T, G and B.
What if, instead of moving your fingers to type ‘hello world’ you just kept your hands in one place and just pressed the relevant fingers? Would the computer be able to make head or tail of what you typed?
I’ve chosen to number the fingers of the left hand, from thumb to little finger L1 to L5 and likewise R1 to R5 for the right. ‘hello world’ would become R2, L3, R4, R4, R4.
The question is, how specific is this? I decided to find out.
The main problem with doing this is that each sequence can produce a large number of potential ‘words’. Many of these are not actually real words. Take the input ‘hello’. This translates to R2, L3, R4, R4, R4 (or, if you typed them on the home row ‘hdlll’). The number of potential words this makes is 6 × 3 × 3 × 3 × 3 = 486. Not all of these are actual words.
So we employ the use of a dictionary. Which informs us that actually only one of these is a real world. So far so good.
I wrote a little program to see what happens with a longer phrase. Take
‘hello my name is joe and this is a bit of an experiment’
This becomes:
‘hdlll jh hajd ks jld ajd fjks ks a gkg lf ah ds;dfkjdhg’
If we run this through a dictionary filter, we get the following possibilities:
hdlll => [‘hello’]
jh => [‘hu’, ‘ju’, ‘mu’, ‘my’, ‘nu’, ‘um’, ‘un’, ‘ym’, ‘yn’]
hajd => [‘haje’, ‘hame’, ‘hand’, ‘jane’, ‘jane’, ‘mand’, ‘mane’, ‘maud’, ‘maud’, ‘name’, ‘nane’, ‘yaje’, ‘yaud’]
ks => [‘is’]
jld => [‘hod’, ‘hoe’, ‘joe’, ‘joe’, ‘mod’, ‘nod’, ‘ule’, ‘yoe’]
ajd => [‘ame’, ‘and’, ‘aye’]
fjks => [‘this’]
ks => [‘is’]
a => [‘a’, ‘a’, ‘q’, ‘q’, ‘z’, ‘z’]
gkg => [‘bib’, ‘big’, ‘bit’, ‘fib’, ‘fig’, ‘fir’, ‘fit’, ‘gib’, ‘gib’, ‘gif’, ‘gig’, ‘git’, ‘rib’, ‘rig’, ‘rit’, ‘tib’, ‘tig’, ‘tit’]
lf => [‘of’, ‘og’, ‘or’]
ah => [‘ah’, ‘ah’, ‘am’, ‘an’, ‘ay’, ‘ay’]
ds;dfkjdhg => [‘experiment’]
With a bit of prediction it could be an effective input method. Markov trees spring to mind. Perhaps if I get some more time I could make it into a viable concept.
I wrote a quick function to check the number collisions (i.e. one word maps to other words). Bearing in mind that for each word that collides with another, there is that other word that collides with it:
Collisions | Frequency |
0 | 199511 |
1 | 19492 |
2 | 6135 |
3 | 3120 |
4 | 1785 |
5 | 1200 |
6 | 770 |
7 | 560 |
8 | 387 |
9 | 390 |
10 | 286 |
11 | 204 |
12 | 208 |
13 | 112 |
14 | 120 |
15 | 64 |
16 | 187 |
17 | 90 |
18 | 57 |
19 | 60 |
20 | 21 |
21 | 44 |
22 | 23 |
23 | 24 |
24 | 25 |
25 | 0 |
26 | 0 |
27 | 0 |
28 | 29 |
29 | 0 |
30 | 0 |
31 | 32 |
Looks like quite a high number of unique words, followed by a significant number of words with a small number of collisions.
And with a log scale
I don’t know about you but I quite like the idea of a one-row keyboard.
Here’s the Python. Do with it as you wish.
# All the fingers
L1, L2, L3, L4, L5, R1, R2, R3, R4, R5 = xrange(10)
# What does each finger type?
types = {}
types[L1] = set(' ')
types[L2] = set('rfvtgb')
types[L3] = set('edc')
types[L4] = set('wsx')
types[L5] = set('qaz')
types[R1] = set(' ')
types[R2] = set('yhnujm')
types[R3] = set('ik,')
types[R4] = set('ol.')
types[R5] = set('p;/')
# What finger for each letter?
letters = {}
for finger, fingerletters in types.items():
for letter in fingerletters:
letters[letter] = finger
# this is where the words are on my mac
wordspath = '/usr/share/dict/words'
# fingers => words
dictionary = {}
def word_to_fingertuple(word):
return tuple([letters[letter] for letter in word])
for word in open(wordspath).readlines():
word = word.strip().lower()
wordfingers = word_to_fingertuple(word)
if not wordfingers in dictionary:
dictionary[wordfingers] = [word]
else:
dictionary[wordfingers].append(word)
def workout(inputstring):
for word in inputstring.split(" "):
wordfingers = word_to_fingertuple(word)
print word
if wordfingers in dictionary:
print dictionary[wordfingers]
def calc_collisions():
# collisions => frequency
histogram = {}
maxval = 0
for word in open(wordspath).readlines():
word = word.strip().lower()
wordfingers = word_to_fingertuple(word)
numcollisions = len(dictionary[wordfingers]) - 1
maxval = max(maxval, numcollisions)
if numcollisions in histogram:
histogram[numcollisions] += 1
else:
histogram[numcollisions] = 1
# fill in zeroes
for i in range(maxval):
if not i in histogram:
histogram[i] = 0
for entry in histogram:
print "%d, %d"%(entry, histogram[entry])
calc_collisions()
workout('hdlll jh hajd ks jld ajd fjks ks a gkg lf ah ds;dfkjdhg')
or
calc_collisions()