Vorschlagsuche (ähnlich zu SMS und T9)

CDW

0
Mitarbeiter
Eingereicht von Ivan Dolvich:
mir ist wieder eine Aufgabe eingefallen, als ich neulich eine SMS im T9 Modus geschrieben habe. Man tippt (je einmal) auf die Tasten 4, 2, 4 und bekommt dann Vorschläge wie "ich", "hai", "gag", "hah" - woher weiss er das?

Weitere Beispiele:

3, 7 -> es, er
8, 6, 7 -> vor, uns
9, 2, 7 -> war, was

Die Aufgabe: Eingabe ist eine Folge von Tasten z.B. 3, 4, 5, 3, 2 und als Ausgabe soll das Programm eine Liste von Wörtern drucken, die diesem Muster entsprechen. Die Wörter kommen aus einer Wortliste, die bekannt ist.

Tipp: Das T9-System macht sich dabei den Umstand zunutze, dass jede der Folgebetätigung der Tasten 2 bis 9 nur einer geringen Anzahl von sinnvollen Wörtern einer bestimmten Sprache, längere Ziffernfolgen sogar oft nur genau einem Wort entsprechen. Mehr Info: http://de.wikipedia.org/wiki/Text_on_9_keys

Beispiel-Wortliste: ich, du, er, sie, es, wir, ihr, mich, dich, ihn, ihr, uns, euch, vor, nach, bei, seit, in, im, an, am, der, die, das, hinter, neben, links, rechts, weg, hier, dort, man, sei, doch, nicht, so, hoch, tief, wie, was, wer, wo, wem, warum, wieso, weshalb, und, los
Ich denke, die Aufgabe sollte mehr oder weniger klar sein. Welche Buchstaben welcher Taste ensprechen, kann man auch bei http://de.wikipedia.org/wiki/Text_on_9_keys anschauen.
 
Ich hab' mal was in Python zusammengebastelt. Von der Wortliste wird erwartet, dass sich jedes Wort in einer eigenen Zeile befindet, weil die meisten Wortlisten nun mal so aufgebaut sind. Wenn sie so wie im Beispiel aussehen soll, kann ich das auch schnell ändern.

Code:
#!/usr/bin/python
import sys

def word2num(word):
	word = word.upper()
	nums = ""
	for id in range(len(word)):
		code = ord(word[id]) - 65
		if (code < 0 or code > 25):
			continue

		if   code < 3:  nums += '2'
		elif code < 6:  nums += '3'
		elif code < 9:  nums += '4'
		elif code < 12: nums += '5'
		elif code < 15: nums += '6'
		elif code < 19: nums += '7'
		elif code < 22: nums += '8'
		else:           nums += '9'
	
	return nums

if len(sys.argv) < 3:
	print "usage: %s <dictionary> <word>" % sys.argv[0]
	sys.exit(1)

dictfile = sys.argv[1]
search   = sys.argv[2]

lines = open(dictfile, 'r').readlines()
for line in lines:
	asNum = word2num(line)
	if asNum == search:
		print line,
 
Hier eine kleine Perl-Lösung mit etwas RegExp:

Code:
#!/usr/bin/perl
# T9 Demo App

my @wordlist = ("ich", "du", "er", "sie", "es", "wir", "ihr", "mich", "dich", "ihn", "ihr", "uns", "euch", "vor", "nach", "bei", "seit", "in", "im", "an", "am", "der", "die", "das", "hinter", "neben", "links", "rechts", "weg", "hier", "dort", "man", "sei", "doch", "nicht", "so", "hoch", "tief", "wie", "was", "wer", "wo", "wem", "warum", "wieso", "weshalb", "und", "los");
my @subst = (" ", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz");
my $i = 1;

print "Bitte T9-Zahlenfolge eingeben:\n";
chomp(my $matcher = <STDIN>);
print "Infrage kommende Woerter:\n";
map { $matcher =~ s/$i/[$_]/g; $i++ } (@subst);
map { print "* $_\n" if /^$matcher$/ } (@wordlist);

mfg, metax.
 
Hi Eydeet

ich glaube du hast mit der Funktion def word2num(word) die Umkehrung der Aufgabe gemacht - von einem Wort die Sequenz herausfinden. Aber ich denke wir brauchen etwas wie def nums2words(nums). Es könnte auch heissen String[] findWordsForNums(int[] nums). Übrigens ist die Umkehrung auch eine sehr nette Aufgabe, ich werde versuchen sie in Groovy zu lösen.

Gruß, Ivan
 
Naja, das Ganze funktioniert schon so, wie es soll.
Die Funktion word2num(word) konvertiert, wie der Name schon andeutet, ein Wort in das Nummern-Äquivalent. Das Programm läuft dann die gesamte Wordlist durch und konvertiert jedes in eine Nummer, die es dann mit der eingegebenen vergleicht.

Ein paar Kommentare im Code hätten vielleicht wirklich nicht geschadet. ;)
Außerdem ist das Ganze auch nicht besonders schön gelöst.
1. unterstützt mein Programm keine Sonderzeichen in Wörtern (d.h. Zeichen außer [a-z])
2. ist das if/elif/else ziemlich hässlich :D

Mfg, Eydeet
 
Original von Eydeet
Naja, das Ganze funktioniert schon so, wie es soll.
Die Funktion word2num(word) konvertiert, wie der Name schon andeutet, ein Wort in das Nummern-Äquivalent. Das Programm läuft dann die gesamte Wordlist durch und konvertiert jedes in eine Nummer, die es dann mit der eingegebenen vergleicht.

Hmm, das ist ein interessanter Ansatz! Zeigt wieder dass ein Problem mehrere Lösungen haben kann :-). Ich eine andere Idee verwendet: am Anfang wählt man alle Wörter aus, dann reduziert man diese Menge bei jede folgende Taste, indem man alle Wörter entfernt, die an der Stelle i keinen entsprechenden Buchstaben haben. Am Ende bleiben nur die gültigen Wörter. Die Lösung wurde mit etwas Groovy und Regex einfacher als ich dachte:
Code:
// the word-list: it contains all known words
def words = ["machen", "gehenn", "ocagem", "mallen", "ocagar"]

// the user input: a sequence of button presses
def input = [6, 2, 2]                   // "machen", "ocagem", "ocagar"

// the mapping of buttons to characters: array-index=button, array-element=charset
//               0       1         2        3        4        5        6        7         8        9
def buttons = ["[ ]", "[.,?!]", "[abc]", "[def]", "[ghi]", "[jkl]", "[mno]", "[pqrs]", "[tuv]", "[wxyz]"]

// take the charsets that correspond to the input and build a regex and a pattern
def regex = input.collect { buttons[it] }.join() + ".*"  // [mno][abc][abc][ghi][def][mno].*
def pattern = ~regex

// use the regex to match the words from the word-list
def result = words.grep(pattern) 
println result

Viele Grüße, Ivan
 
Hallo!

Meine Java Lösung

SmsMain.java
Code:
public class SmsMain {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println(new T9().getWordsByKeys("4"));
		System.out.println(new T9().getWordsByKeys("4 3"));
		System.out.println(new T9().getWordsByKeys("7 7 6"));
	}

}

T9.java
Code:
public class T9 {

	private WordList wordList = null;
	private ArrayList<String> keySet = null;
	
	public T9() {
		this.wordList = new WordList();
		this.keySet = new ArrayList<String>();
		this.init();
	}
	
	private void init() {
		this.keySet.add("[ ]");
		this.keySet.add("[.,?!]");
		this.keySet.add("[abc]");
		this.keySet.add("[def]");
		this.keySet.add("[ghi]");
		this.keySet.add("[jkl]");
		this.keySet.add("[mno]");
		this.keySet.add("[pqrs]");
		this.keySet.add("[tuv]");
		this.keySet.add("[wxyz]");
	}
	
	public String getWordsByKeys(String value) {
		String regex = "";
		for(int i=0; i<value.split(" ").length; i++)
			regex += keySet.get(Integer.parseInt(value.split(" ")[i])) + ".*";
		return Arrays.toString(wordList.getWords(regex).toArray());
	}
	
}

WordList.java
Code:
public class WordList {

	private ArrayList<String> words = null;

	public WordList() {
		this.words = new ArrayList<String>();
		this.init();
	}
	
	private void init() {
		this.words.add("ich");
		this.words.add("programmiere");
		this.words.add("ein"); 
		this.words.add("paar");
		this.words.add("aufgaben");
		this.words.add("aus");
		this.words.add("dem");
		this.words.add("hackerboard");
		this.words.add("nach");
	}
	
	public ArrayList<String> getWords(String regex) {
		ArrayList<String> wordsMatch = new ArrayList<String>();
		for(String word : words) {
			if(word.matches(regex))
				wordsMatch.add(word);
		}
		return wordsMatch;
	}
	
}

Gruß
Felix
 
Code:
:-use_module(library(lists)).
:-dynamic dict/2.

key(Char,Key):-nth1(Num,[" ","abcABC","defDEF","ghiGHI","jklJKL","mnoMNO",
			 "pqrsPQRS","tuvTUV","wxyzWXYZ"],Seq), member(Char,Seq),!,
                         number_chars(Num,[Key]). 

word2key(Word,Key):-maplist(key,Word,Key).

append_([X],[X|_]).
append_([H|T],[H|T2]):-append_(T,T2).

t9(Num):-number_chars(Num,Chars),append_(Chars,Key),
	findall(_,(dict(Key,Word),(atom_codes(A,Word),write(A),nl)),_).

read_dict(File):-retractall(dict(_,_)),
	open(File,read,Stream),
	repeat,
	    read_line(Stream,Word),	                 %SWI: read_line_to_codes(Stream,Line)
	   (word2key(Word,Key) -> assertz(dict(Key,Word))
	                        ; true),
        Word=end_of_file,
	close(Stream).
Wortliste einlesen (Format: wie üblich, ein Wort pro Zeile):
Code:
?- read_dict('test.txt').
yes
| ?-
Abfragen:
Code:
 t9(3).
du
er
es
dich
euch
der
die
das
dort
doch
?- t9(34).
dich
die
yes
| ?- t9(342).
dich
yes
 
Zurück
Oben