Abbreviations, simple

From Rosetta Code
Task
Abbreviations, simple
You are encouraged to solve this task according to the task description, using any language you may know.

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



ALGOL 68[edit]

Works with: ALGOL 68G version Any - tested with release 2.8.3.win32
# "Simple" abbreviations                                               #
 
# returns the next word from text, updating pos #
PRIO NEXTWORD = 1;
OP NEXTWORD = ( REF INT pos, STRING text )STRING:
BEGIN
# skip spaces #
WHILE IF pos > UPB text THEN FALSE ELSE text[ pos ] = " " FI DO pos +:= 1 OD;
# get the word #
STRING word := "";
WHILE IF pos > UPB text THEN FALSE ELSE text[ pos ] /= " " FI DO
word +:= text[ pos ];
pos +:= 1
OD;
word
END # NEXTWORD # ;
# returns text converted to upper case #
OP TOUPPER = ( STRING text )STRING:
BEGIN
STRING result := text;
FOR ch pos FROM LWB result TO UPB result DO
IF is lower( result[ ch pos ] ) THEN result[ ch pos ] := to upper( result[ ch pos ] ) FI
OD;
result
END # TOUPPER # ;
# returns text converted to an INT or -1 if text is not a number #
OP TOINT = ( STRING text )INT:
BEGIN
INT result := 0;
BOOL is numeric := TRUE;
FOR ch pos FROM UPB text BY -1 TO LWB text WHILE is numeric DO
CHAR c = text[ ch pos ];
is numeric := ( c >= "0" AND c <= "9" );
IF is numeric THEN ( result *:= 10 ) +:= ABS c - ABS "0" FI
OD;
IF is numeric THEN result ELSE -1 FI
END # TOINT # ;
# returns the length of word #
OP LENGTH = ( STRING word )INT: 1 + ( UPB word - LWB word );
# counts the number of commands in commands #
PROC count commands = ( STRING commands )INT:
BEGIN
INT result := 0;
INT pos := LWB commands;
WHILE STRING command := pos NEXTWORD commands; command /= "" DO
IF TOINT command < 0 THEN
# not an abbreviation length #
result +:= 1
FI
OD;
result
END # count commands # ;
 
# list of "commands" - the words are optionally followed by the minimum #
# length of abbreviation - if there isn't a number #
# the command can only appear in full #
STRING commands
= "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 "
;
# build the tables of the commands and their minimum lengths #
PROC load commands = ( STRING commands )VOID:
BEGIN
INT cmd pos := 0;
INT pos := LWB command table;
WHILE STRING command := pos NEXTWORD commands; command /= "" DO
INT len := TOINT command;
IF len >= 0 THEN
# have an abbreviation length #
IF cmd pos > 0 THEN min abbreviation[ cmd pos ] := len FI
ELSE
# new command #
cmd pos +:= 1;
command table[ cmd pos ] := TOUPPER command;
min abbreviation[ cmd pos ] := LENGTH command
FI
OD
END # load commands # ;
# searches for word in command table and returns the full command #
# matching the possible abbreviation or *error* if there is no match #
OP EXPAND = ( STRING word )STRING:
IF word = ""
THEN # empty word #
""
ELSE # non-empty word #
INT word len = LENGTH word;
STRING upper word := TOUPPER word;
STRING result := "*error*";
FOR cmd pos FROM LWB command table TO UPB command table
WHILE STRING command := command table[ cmd pos ];
IF word len < min abbreviation[ cmd pos ] OR word len > LENGTH command
THEN # word is too short or too long - try the next command #
TRUE
ELIF upper word = command[ LWB command : ( LWB command - 1 ) + word len ]
THEN # found the command #
result := command;
FALSE
ELSE # word doexn't match - try the next command #
TRUE
FI
DO SKIP OD;
result
FI # EXPAND # ;
 
# tests the EXPAND operator #
PROC test expand = ( STRING words )VOID:
BEGIN
STRING results := "", separator := "";
INT pos := LWB words;
WHILE STRING word = pos NEXTWORD words; word /= "" DO
results +:= separator + EXPAND word;
separator := " "
OD;
print( ( "Input: ", words, newline ) );
print( ( "Output: ", results, newline ) )
END # test expand # ;
 
# build the command table #
[ 1 : count commands( commands ) ]STRING command table;
[ 1 : UPB command table ]INT min abbreviation;
load commands( commands );
 
# task test cases #
test expand( "riG rePEAT copies put mo rest types fup. 6 poweRin" )
Output:
Input:  riG   rePEAT copies  put mo   rest    types   fup.    6       poweRin
Output: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT

Factor[edit]

USING: arrays assocs combinators formatting fry grouping.extras
kernel literals math math.parser multiline sequences
splitting.extras unicode ;
IN: rosetta-code.abbr-simple
 
CONSTANT: input $[
"riG rePEAT copies put mo rest types fup. 6 "
"poweRin" append
]
 
<<  ! Make the following two words available at parse time.
 
: abbr-pair ( first second -- seq/f )
{
{ [ 2dup drop [ digit? ] all? ] [ 2drop f ] }
{
[ 2dup nip [ Letter? ] all? ]
[ drop >upper 0 2array ]
}
[ [ >upper ] [ string>number ] bi* 2array ]
} cond ;
 
: parse-commands ( seq -- seq )
" \n" split-harvest [ abbr-pair ] 2clump-map sift ;
 
>>
 
CONSTANT: commands $[
HEREDOC: END
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
END
parse-commands
]
 
: valid-abbrevs ( assoc seq -- assoc )
dup '[ [ _ head? ] [ _ length <= ] bi* and ] assoc-filter ;
 
: find-command ( seq -- seq )
>upper [ commands ] dip valid-abbrevs
[ "*error*" ] [ first first ] if-empty ;
 
: (find-commands) ( seq -- seq )
" " split-harvest [ find-command ] map " " join ;
 
: find-commands ( seq -- seq )
dup empty? not [ (find-commands) ] when ;
 
: show-commands ( seq -- )
dup find-commands " Input: \"%s\"\nOutput: \"%s\"\n" printf
 ;
 
: main ( -- ) input "" [ show-commands ] [email protected] ;
 
MAIN: main
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: ""

Forth[edit]

Works with any ANS Forth

Needs the FMS-SI (single inheritance) library code located here: http://soton.mpeforth.com/flag/fms/index.html

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 [email protected] 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
 
 

Go[edit]

Translation of: Kotlin
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, " "))
}
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[edit]

Constructing a prefix tree (or trie) for validation and expansion:

(() => {
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;
},
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, chars(toUpper(s)));
};
 
// 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();
})();
Output:
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT


Julia[edit]

 
const commandtable = """
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"""
 
function makedict(tbl)
str = split(uppercase(tbl), r"\s+")
dict = Dict{String, String}()
for (i, s) in enumerate(str)
if (n = tryparse(Int, s)) != nothing
dict[str[i-1][1:n]] = str[i-1]
else
dict[s] = s
end
end
dict
end
 
function okabbrev(dict, abb)
for i in length(abb):-1:1
if haskey(dict, abb[1:i])
com = dict[abb[1:i]]
if length(abb) <= length(com) && abb == com[1:length(abb)]
return dict[abb[1:i]]
end
end
end
return "*error*"
end
 
formattedprint(arr, n) = (for s in arr print(rpad(s, n)) end; println())
 
function teststring(str)
d = makedict(commandtable)
commands = split(str, r"\s+")
formattedprint(commands, 9)
formattedprint([okabbrev(d, uppercase(s)) for s in commands], 9)
end
 
teststring("riG rePEAT copies put mo rest types fup. 6 poweRin")
 
Output:

riG rePEAT copies put mo rest types fup. 6 poweRin RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT

Kotlin[edit]

// 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()
}
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[edit]

Translation of: Perl 6
@c = (uc join ' ', qw<
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
>) =~ /([a-zA-Z]+(?:\s+\d+)?)(?=\s+[a-zA-Z]|$)/g;
 
my %abr = ('' => '', ' ' => '');
for (@c) {
($w,$sl) = split ' ', $_;
$ll = length($w);
$sl = $ll unless $sl;
$abr{substr($w,0,$sl)} = $w;
map { $abr{substr($w, 0, $_)} = $w } $sl .. $ll;
}
 
$fmt = "%-10s";
$inp = sprintf $fmt, 'Input:';
$out = sprintf $fmt, 'Output:';
for $str ('', qw<riG rePEAT copies put mo rest types fup. 6 poweRin>) {
$inp .= sprintf $fmt, $str;
$out .= sprintf $fmt, $abr{uc $str} // '*error*';
}
 
print "$inp\n$out\n";
Output:
Input:              riG       rePEAT    copies    put       mo        rest      types     fup.      6         poweRin
Output:             RIGHT     REPEAT    *error*   PUT       MOVE      RESTORE   *error*   *error*   *error*   POWERINPUT

Perl 6[edit]

Works with: Rakudo version 2017.08

Demonstrate that inputting an empty string returns an empty string in addition to the required test input.

<
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.trim.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;
}
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[edit]

Works with: Python version 3.6
 
 
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)
 
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[edit]

#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 "") ""))
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[edit]

/*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. */
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[edit]

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();
Output:
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT