Abbreviations, simple: Difference between revisions
m (→{{header|JavaScript}}: (Added some comments to the validation lines)) |
|||
Line 287: | Line 287: | ||
) |
) |
||
); |
); |
||
// -> |
// -> RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT |
||
}; |
}; |
||
Line 293: | Line 293: | ||
const validation = trie => s => { |
const validation = trie => s => { |
||
const go = (trees, cs) => |
const go = (trees, cs) => |
||
isNull(cs) ? Just( |
isNull(cs) ? Just( // Completed input matches any expansions ? |
||
concatMap( |
concatMap( |
||
x => { |
x => { |
||
Line 304: | Line 304: | ||
) |
) |
||
) : bindMay( |
) : bindMay( |
||
find( |
find( // Any trie branch with a matching root ? |
||
tree => isPrefixOf(head(cs), tree.root), |
tree => isPrefixOf(head(cs), tree.root), |
||
trees |
trees |
||
), |
), |
||
tree => { |
tree => { |
||
const |
const // Hyphen splits minimum length from rest. |
||
parts = tree.root.split('-'), |
parts = tree.root.split('-'), |
||
k = chars(fst(parts)) |
k = chars(fst(parts)); |
||
⚫ | |||
return 1 < parts.length ? ( |
return 1 < parts.length ? ( // Hyphenated string in trie ? |
||
⚫ | |||
isPrefixOf(k, cs) ? ( // A match up to min length ? |
|||
⚫ | |||
isPrefixOf( // Rest of input matches ? |
|||
⚫ | |||
chars(snd(parts)) |
chars(snd(parts)) |
||
⚫ | |||
) ? ( // No clash beyond min length. Expandable. |
|||
Just(parts.join('')) |
Just(parts.join('')) |
||
) : Nothing() |
|||
) : Nothing() |
) : Nothing() // Match failure after min length. |
||
) : isPrefixOf(k, cs) ? ( |
|||
) : Nothing() // Match failure before min length. |
|||
go(tree.nest, drop(lng, cs)), |
|||
⚫ | |||
bindMay( // then further recursion down the trie ... |
|||
⚫ | |||
xs => Just(k.concat(xs).join('')) |
xs => Just(k.concat(xs).join('')) |
||
) |
) |
||
) : Nothing(); |
) : Nothing(); // No match between trie string and input. |
||
} |
} |
||
); |
); |
Revision as of 15:20, 12 August 2018
The use of abbreviations (also sometimes called synonyms, nicknames, AKAs, or aliases) can be an
easy way to add flexibility when specifying or using commands, sub─commands, options, etc.
For this task, the following command table will be used:
add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate 3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left 2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1
Notes concerning the above command table:
- it can be thought of as one long literal string (with blanks at end-of-lines)
- it may have superfluous blanks
- it may be in any case (lower/upper/mixed)
- the order of the words in the command table must be preserved as shown
- the user input(s) may be in any case (upper/lower/mixed)
- commands will be restricted to the Latin alphabet (A ──► Z, a ──► z)
- a command is followed by an optional number, which indicates the minimum abbreviation
- A valid abbreviation is a word that has:
- at least the minimum length of the word's minimum number in the command table
- compares equal (regardless of case) to the leading characters of the word in the command table
- a length not longer than the word in the command table
- ALT, aLt, ALTE, and ALTER are all abbreviations of ALTER 3
- AL, ALF, ALTERS, TER, and A aren't valid abbreviations of ALTER 3
- The 3 indicates that any abbreviation for ALTER must be at least three characters
- Any word longer than five characters can't be an abbreviation for ALTER
- o, ov, oVe, over, overL, overla are all acceptable abbreviations for overlay 1
- if there isn't a number after the command, then there isn't an abbreviation permitted
- Task
-
- The command table needn't be verified/validated.
- Write a function to validate if the user "words" (given as input) are valid (in the command table).
- If the word is valid, then return the full uppercase version of that "word".
- If the word isn't valid, then return the lowercase string: *error* (7 characters).
- A blank input (or a null input) should return a null string.
- Show all output here.
- An example test case to be used for this task
For a user string of:
riG rePEAT copies put mo rest types fup. 6 poweRin
the computer program should return the string:
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
- Related tasks
Forth
Works with any ANS Forth
Needs the FMS-SI (single inheritance) library code located here: http://soton.mpeforth.com/flag/fms/index.html <lang forth>include FMS-SI.f include FMS-SILib.f
${ add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate 3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left 2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1 } value list ' upper: list map:
- compare' { adr len $obj -- f }
len $obj size: > if false exit then adr len $obj @: drop len compare 0= ;
- <= ( n1 n2 = f) 1+ swap > ;
- abbrev 0 0 { adr len obj1 obj2 -- }
list uneach: list each: drop to obj1 begin list each: while to obj2 obj2 @: >integer if \ word followed by a number len <= if adr len obj1 compare' if obj1 p: exit then then list each: if to obj1 else ." *error* " exit then else \ word not followed by a number adr len obj1 @: compare 0= if obj1 p: exit then obj2 to obj1 then repeat ;
${ riG rePEAT copies put mo rest types fup. 6 poweRin }
value input-list ' upper: input-list map:
- valid-input ( adr len -- f)
over + swap do i c@ isalpha 0= if ." *error* " unloop false exit then loop true ;
- run
begin input-list each: while dup @: valid-input if @: abbrev space else drop then repeat ;
run RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT ok
</lang>
Go
<lang go>package main
import(
"fmt" "strconv" "strings"
)
var table =
"add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 " + "compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate " + "3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 " + "forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load " + "locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 " + "msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 " + "refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left " + "2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1 "
func minOf(x, y int) int {
if x < y { return x } return y
}
func validate(commands, words []string, minLens []int) []string {
results := make([]string, 0) if len(words) == 0 { return results } for _, word := range words { matchFound := false wlen := len(word) for i, command := range commands { if minLens[i] == 0 || wlen < minLens[i] || wlen > len(command) { continue } c := strings.ToUpper(command) w := strings.ToUpper(word) if strings.HasPrefix(c, w) { results = append(results, c) matchFound = true break } } if !matchFound { results = append(results, "*error*") } } return results
}
func main() {
table = strings.TrimSpace(table) splits := strings.Fields(table) slen := len(splits) commands := make([]string, 0, slen / 2) // capacity approximate minLens := make([]int, 0, slen / 2) // ditto i := 0 for { commands = append(commands, splits[i]) len := len(splits[i]) if i == slen - 1 { minLens = append(minLens, len) break } i++ num, err := strconv.Atoi(splits[i]) if err == nil { minLens = append(minLens, minOf(num, len)) i++ if i == slen { break } } else { minLens = append(minLens, len) } } sentence := "riG rePEAT copies put mo rest types fup. 6 poweRin" words := strings.Fields(sentence) results := validate(commands, words, minLens) fmt.Print("user words: ") for j := 0; j < len(words); j++ { fmt.Printf("%-*s ", len(results[j]), words[j]) } fmt.Print("\nfull words: ") fmt.Println(strings.Join(results, " "))
}</lang>
- Output:
user words: riG rePEAT copies put mo rest types fup. 6 poweRin full words: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
JavaScript
Constructing a prefix tree (or trie) for validation and expansion: <lang javascript>(() => {
const main = () => {
const strTable = ` add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate 3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left 2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1`;
// https://en.wikipedia.org/wiki/Trie const trie = prefixTree(strTable); return unwords( map(w => { const maybe = validation(trie)(w); return maybe.Nothing ? ( '*error*' ) : maybe.Just; }, map(toUpper, words( 'riG rePEAT copies put mo rest ' + 'types fup. 6 poweRin' ) ) ) ); // -> RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT };
// validation :: Tree String -> String -> Maybe String const validation = trie => s => { const go = (trees, cs) => isNull(cs) ? Just( // Completed input matches any expansions ? concatMap( x => { const k = x.root; return isPrefixOf('-', k) ? ( [k.slice(1)] ) : []; }, trees ) ) : bindMay( find( // Any trie branch with a matching root ? tree => isPrefixOf(head(cs), tree.root), trees ), tree => { const // Hyphen splits minimum length from rest. parts = tree.root.split('-'), k = chars(fst(parts));
return 1 < parts.length ? ( // Hyphenated string in trie ?
isPrefixOf(k, cs) ? ( // A match up to min length ?
isPrefixOf( // Rest of input matches ? drop(k.length, cs), chars(snd(parts))
) ? ( // No clash beyond min length. Expandable. Just(parts.join())
) : Nothing() // Match failure after min length.
) : Nothing() // Match failure before min length.
) : isPrefixOf(k, cs) ? ( // If trie string is a prefix, bindMay( // then further recursion down the trie ... go(tree.nest, drop(k.length, cs)), xs => Just(k.concat(xs).join()) ) ) : Nothing(); // No match between trie string and input. } ); return go(trie, s.split()); };
// prefixTree :: String :: Tree Char const prefixTree = strTable => trieFromStrings( sort(fst(foldl( (a, x) => { const n = isNaN(x) ? 0 : parseInt(x, 10), k = snd(a); return 0 < n ? ( Tuple( fst(a).concat( take(n, k) + '-' + drop(n, k) ), ) ) : Tuple( isNull(k) ? ( fst(a) ) : fst(a).concat(k), x ) }, Tuple([], ), map(toUpper, words(strTable)) ))) );
// GENERIC STRING TRIES -------------------------------
// https://en.wikipedia.org/wiki/Trie
// trieFromStrings :: [String] -> [Tree String] const trieFromStrings = xs => { const go = tree => { const root = tree.root, nest = tree.nest; return 1 === nest.length ? (() => { const child = nest[0]; return go(Node( root + child.root, child.nest.map(go) )); })() : Node( root, map(go, nest) ); }; return charTrie(xs).map(go); };
// charTrie = [String] -> [Tree String] const charTrie = xs => { const go = ks => concatMap( gp => 0 < gp.length ? (() => { const rest = concatMap( x => 0 < x.length ? ( [x.slice(1)] ) : [], gp ); return 1 < rest.length ? ( Node( gp[0][0].toUpperCase(), go(rest) ) ) : Node(gp[0].toUpperCase(), []); })() : gp, 1 < ks.length ? ( groupBy(on(eq, head), ks) ) : [ks] ); return go(xs); };
// GENERIC FUNCTIONS ----------------------------------
// Just :: a -> Just a const Just = x => ({ type: 'Maybe', Nothing: false, Just: x });
// Node :: a -> [Tree a] -> Tree a const Node = (v, xs) => ({ type: 'Node', root: v, nest: xs || [] });
// Nothing :: () -> Nothing const Nothing = () => ({ type: 'Maybe', Nothing: true, });
// Tuple (,) :: a -> b -> (a, b) const Tuple = (a, b) => ({ type: 'Tuple', '0': a, '1': b, length: 2 });
// bindMay (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b const bindMay = (mb, mf) => mb.Nothing ? mb : mf(mb.Just);
// chars :: String -> [Char] const chars = s => s.split();
// concatMap :: (a -> [b]) -> [a] -> [b] const concatMap = (f, xs) => 0 < xs.length ? (() => { const unit = 'string' !== typeof xs ? ( [] ) : ; return unit.concat.apply(unit, xs.map(f)) })() : [];
// drop :: Int -> [a] -> [a] // drop :: Int -> String -> String const drop = (n, xs) => xs.slice(n);
// enumFromThenToInt :: Int -> Int -> Int -> [Int] const enumFromThenToInt = (x1, x2, y) => { const d = x2 - x1; return Array.from({ length: Math.floor(y - x2) / d + 2 }, (_, i) => x1 + (d * i)); };
// eq (==) :: Eq a => a -> a -> Bool const eq = (a, b) => { const t = typeof a; return t !== typeof b ? ( false ) : 'object' !== t ? ( 'function' !== t ? ( a === b ) : a.toString() === b.toString() ) : (() => { const aks = Object.keys(a); return aks.length !== Object.keys(b).length ? ( false ) : aks.every(k => eq(a[k], b[k])); })(); };
// find :: (a -> Bool) -> [a] -> Maybe a const find = (p, xs) => { for (let i = 0, lng = xs.length; i < lng; i++) { let x = xs[i]; if (p(x)) return Just(x); } return Nothing(); };
// foldl :: (a -> b -> a) -> a -> [b] -> a const foldl = (f, a, xs) => xs.reduce(f, a);
// fst :: (a, b) -> a const fst = tpl => tpl[0];
// Typical usage: groupBy(on(eq, f), xs)
// groupBy :: (a -> a -> Bool) -> [a] -> a const groupBy = (f, xs) => { const tpl = xs.slice(1) .reduce((a, x) => { const h = a[1].length > 0 ? a[1][0] : undefined; return (undefined !== h) && f(h, x) ? ( Tuple(a[0], a[1].concat([x])) ) : Tuple(a[0].concat([a[1]]), [x]); }, Tuple([], 0 < xs.length ? [xs[0]] : [])); return tpl[0].concat([tpl[1]]); };
// head :: [a] -> a const head = xs => xs.length ? xs[0] : undefined;
// isNull :: [a] -> Bool // isNull :: String -> Bool const isNull = xs => Array.isArray(xs) || ('string' === typeof xs) ? ( 1 > xs.length ) : undefined;
// isPrefixOf takes two lists or strings and returns // true iff the first is a prefix of the second.
// isPrefixOf :: [a] -> [a] -> Bool // isPrefixOf :: String -> String -> Bool const isPrefixOf = (xs, ys) => { const pfx = (xs, ys) => { const intX = xs.length; return 0 < intX ? ( ys.length >= intX ? xs[0] === ys[0] && pfx( xs.slice(1), ys.slice(1) ) : false ) : true; }; return 'string' !== typeof xs ? ( pfx(xs, ys) ) : ys.startsWith(xs); };
// map :: (a -> b) -> [a] -> [b] const map = (f, xs) => xs.map(f);
// e.g. sortBy(on(compare,length), xs)
// on :: (b -> b -> c) -> (a -> b) -> a -> a -> c const on = (f, g) => (a, b) => f(g(a), g(b));
// snd :: (a, b) -> b const snd = tpl => tpl[1];
// sort :: Ord a => [a] -> [a] const sort = xs => xs.slice() .sort((a, b) => a < b ? -1 : (a > b ? 1 : 0));
// take :: Int -> [a] -> [a] const take = (n, xs) => xs.slice(0, n);
// toUpper :: String -> String const toUpper = s => s.toUpperCase();
// unwords :: [String] -> String const unwords = xs => xs.join(' ');
// words :: String -> [String] const words = s => s.split(/\s+/);
// MAIN --- return main();
})();</lang>
- Output:
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Kotlin
<lang scala>// version 1.1.4-3
val r = Regex("[ ]+")
val table =
"add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 " + "compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate " + "3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 " + "forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load " + "locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 " + "msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 " + "refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left " + "2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1 "
fun validate(commands: List<String>, minLens: List<Int>, words: List<String>): List<String> {
if (words.isEmpty()) return emptyList<String>() val results = mutableListOf<String>() for (word in words) { var matchFound = false for ((i, command) in commands.withIndex()) { if (minLens[i] == 0 || word.length !in minLens[i] .. command.length) continue if (command.startsWith(word, true)) { results.add(command.toUpperCase()) matchFound = true break } } if (!matchFound) results.add("*error*") } return results
}
fun main(args: Array<String>) {
val splits = table.trimEnd().split(r) val commands = mutableListOf<String>() val minLens = mutableListOf<Int>() var i = 0 while (true) { commands.add(splits[i]) val len = splits[i].length if (i == splits.size - 1) { minLens.add(len) break } val num = splits[++i].toIntOrNull() if (num != null) { minLens.add(minOf(num, len)) if (++i == splits.size) break } else minLens.add(len) } val sentence = "riG rePEAT copies put mo rest types fup. 6 poweRin" val words = sentence.trim().split(r) val results = validate(commands, minLens, words) print("user words: ") for (j in 0 until words.size) print("${words[j].padEnd(results[j].length)} ") print("\nfull words: ") for (j in 0 until results.size) print("${results[j]} ") println()
}</lang>
- Output:
user words: riG rePEAT copies put mo rest types fup. 6 poweRin full words: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Perl 6
Demonstrate that inputting an empty string returns an empty string in addition to the required test input.
<lang perl6>< add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate 3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left 2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1 > ~~ m:g/ (<.alpha>+) \s* (\d*) /;
my %abr = => , |$/.map: {
my $abbrv = $_[0].Str.fc; |map { $abbrv.substr( 0, $_ ) => $abbrv.uc }, +($_[1].Str || $_[0].Str.chars) .. $_[0].Str.chars;
};
sub abbr-simple ( $str ) { %abr{$str.fc} // '*error*' }
- Testing
for 'riG rePEAT copies put mo rest types fup. 6 poweRin', -> $str {
put ' Input: ', $str; put 'Output: ', join ' ', $str.words.map: *.&abbr-simple;
}</lang>
- Output:
Input: riG rePEAT copies put mo rest types fup. 6 poweRin Output: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT Input: Output:
Python
<lang python>
command_table_text = """add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate 3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left 2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1"""
user_words = "riG rePEAT copies put mo rest types fup. 6 poweRin"
def find_abbreviations_length(command_table_text):
""" find the minimal abbreviation length for each word. a word that does not have minimum abbreviation length specified gets it's full lengths as the minimum. """ command_table = dict() input_iter = iter(command_table_text.split())
word = None try: while True: if word is None: word = next(input_iter) abbr_len = next(input_iter, len(word)) try: command_table[word] = int(abbr_len) word = None except ValueError: command_table[word] = len(word) word = abbr_len except StopIteration: pass return command_table
def find_abbreviations(command_table):
""" for each command insert all possible abbreviations""" abbreviations = dict() for command, min_abbr_len in command_table.items(): for l in range(min_abbr_len, len(command)+1): abbr = command[:l].lower() abbreviations[abbr] = command.upper() return abbreviations
def parse_user_string(user_string, abbreviations):
user_words = [word.lower() for word in user_string.split()] commands = [abbreviations.get(user_word, "*error*") for user_word in user_words] return " ".join(commands)
command_table = find_abbreviations_length(command_table_text)
abbreviations_table = find_abbreviations(command_table)
full_words = parse_user_string(user_words, abbreviations_table)
print("user words:", user_words) print("full words:", full_words) </lang>
- Output:
input: "" output: "" input: "riG rePEAT copies put mo rest types fup. 6 poweRin" output: "RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT"
Racket
<lang racket>#lang racket (require srfi/13)
(define command-table #<<EOS add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate 3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left 2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1 EOS
)
(define command/abbr-length-pairs
(let loop ((cmd-len-list (string-split command-table)) (acc (list))) (match cmd-len-list ((list) (sort acc < #:key cdr)) ((list-rest a (app string->number (and ad (not #f))) dd) (loop dd (cons (cons a ad) acc))) ((list-rest a d) (loop d (cons (cons a (string-length a)) acc))))))
(define (validate-word w)
(or (let ((w-len (string-length w))) (for/first ((candidate command/abbr-length-pairs) #:when (and (>= w-len (cdr candidate)) (string-prefix-ci? w (car candidate)))) (string-upcase (car candidate)))) "*error*"))
(define (validate-string s) (string-join (map validate-word (string-split s))))
(module+ main
(define (debug-i/o s) (printf "input: ~s~%output: ~s~%" s (validate-string s))) (debug-i/o "") (debug-i/o "riG rePEAT copies put mo rest types fup. 6 poweRin"))
(module+ test
(require rackunit) (check-equal? (validate-string "riG rePEAT copies put mo rest types fup. 6 poweRin") "RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT") (check-equal? (validate-string "") ""))</lang>
- Output:
input: "" output: "" input: "riG rePEAT copies put mo rest types fup. 6 poweRin" output: "RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT"
REXX
<lang rexx>/*REXX program validates a user "word" against a "command table" with abbreviations.*/ parse arg uw /*obtain optional arguments from the CL*/ if uw= then uw= 'riG rePEAT copies put mo rest types fup. 6 poweRin' say 'user words: ' uw
@= 'add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3',
'compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate', '3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2', 'forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load', 'locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2', 'msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3', 'refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left', '2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1'
say 'full words: ' validate(uw) /*display the result(s) to the terminal*/ exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ validate: procedure expose @; arg x; upper @ /*ARG capitalizes all the X words. */
$= /*initialize the return string to null.*/ do j=1 to words(x); _=word(x, j) /*obtain a word from the X list. */ do k=1 to words(@); a=word(@, k) /*get a legitmate command name from @.*/ L=word(@, k+1) /*··· and maybe get it's abbrev length.*/ if datatype(L, 'W') then k=k + 1 /*yuppers, it's an abbrev length.*/ else L=length(a) /*nope, it can't be abbreviated.*/ if abbrev(a, _, L) then do; $=$ a; iterate j; end /*is valid abbrev?*/ end /*k*/ $=$ '*error*' /*processed the whole list, not valid. */ end /*j*/ return strip($) /*elide the superfluous leading blank. */</lang>
- output when using the default input:
user words: riG rePEAT copies put mo rest types fup. 6 poweRin full words: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
zkl
<lang zkl>commands:=Data(0,String, // "add\01\0alter\0..."
- <<<
"add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate 3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left 2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1" .toUpper().split());
- <<<
var szs=Dictionary(); // [<index>:<length> ...] n:=0; while(n<commands.len()){
cmd,nc := commands.readString(n), n + cmd.len() + 1; len:=commands.readString(nc); if(len.matches("[0-9]*")){ szs[n]=len.toInt(); n=nc+len.len()+1 } else { szs[n]=cmd.len(); n=nc; }
}
testText:="riG rePEAT copies put mo rest types "
" fup. 6 poweRin";
testText.split().apply('wrap(w){
w=w.toUpper(); n:=0; while(True){ // check for length requirement and, if there, verify n=commands.find(w,n); if(Void==n) return("*error*"); // end of loop if no match c:=commands.readString(n); if(w.len()>=szs.find(n,99999)) return(c); n+=c.len(); }
}).concat(" ").println();</lang>
- Output:
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT