Abbreviations, simple: Difference between revisions
(→Python - Composition of pure functions: Moved definition of an expanded empty string) |
m (→Python - composition of pure functions: Tidying) |
||
Line 1,303: | Line 1,303: | ||
'*error*' string. |
'*error*' string. |
||
''' |
''' |
||
return lambda s: ( |
return lambda s: unwords(map( |
||
expanded(table), words(s) |
|||
⚫ | |||
expanded(table), |
|||
⚫ | |||
)) |
|||
⚫ | |||
Line 1,317: | Line 1,314: | ||
strings and minimum abbreviation lengths. |
strings and minimum abbreviation lengths. |
||
''' |
''' |
||
def |
def expand(k): |
||
u = k.upper() |
u = k.upper() |
||
lng = len(k) |
lng = len(k) |
||
Line 1,325: | Line 1,322: | ||
return w.startswith(u) and lng >= n |
return w.startswith(u) and lng >= n |
||
return find(p)(table) if k else Just(('', 0)) |
return find(p)(table) if k else Just(('', 0)) |
||
return lambda s: maybe('*error*')(fst)( |
return lambda s: maybe('*error*')(fst)(expand(s)) |
||
go(s) |
|||
) |
|||
Line 1,446: | Line 1,442: | ||
f(m.get('Just')) |
f(m.get('Just')) |
||
) |
) |
||
# unwords :: [String] -> String |
|||
def unwords(xs): |
|||
'''A space-separated string derived from |
|||
a list of words.''' |
|||
return ' '.join(xs) |
|||
# words :: String -> [String] |
|||
def words(s): |
|||
'''A list of words delimited by characters |
|||
representing white space.''' |
|||
⚫ | |||
Revision as of 12:37, 15 April 2019
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
ALGOL 68
<lang algol68># "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" )</lang>
- Output:
Input: riG rePEAT copies put mo rest types fup. 6 poweRin Output: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Factor
<lang factor>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 ] bi@ ;
MAIN: main</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: ""
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 ( "io" "os" "strconv" "strings" "text/tabwriter" )
func readTable(table string) ([]string, []int) { fields := strings.Fields(table) var commands []string var minLens []int
for i, max := 0, len(fields); i < max; { cmd := fields[i] cmdLen := len(cmd) i++
if i < max { num, err := strconv.Atoi(fields[i]) if err == nil && 1 <= num && num < cmdLen { cmdLen = num i++ } }
commands = append(commands, cmd) minLens = append(minLens, cmdLen) }
return commands, minLens }
func validateCommands(commands []string, minLens []int, words []string) []string { var results []string 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 printResults(words []string, results []string) { wr := tabwriter.NewWriter(os.Stdout, 0, 1, 1, ' ', 0) io.WriteString(wr, "user words:") for _, word := range words { io.WriteString(wr, "\t"+word) } io.WriteString(wr, "\n") io.WriteString(wr, "full words:\t"+strings.Join(results, "\t")+"\n") wr.Flush() }
func main() { const 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 "
const sentence = "riG rePEAT copies put mo rest types fup. 6 poweRin"
commands, minLens := readTable(table) words := strings.Fields(sentence)
results := validateCommands(commands, minLens, words)
printResults(words, 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
Haskell
<lang haskell>import Data.List (find, isPrefixOf) import Data.Char (isDigit, toUpper) import Data.Maybe (maybe)
withExpansions :: [(String, Int)] -> String -> String withExpansions tbl s = unwords $ expanded tbl <$> words s
expanded :: [(String, Int)] -> String -> String expanded tbl k = maybe "*error" fst (expand k)
where expand [] = Just ([], 0) expand s = let u = toUpper <$> s lng = length s in find (\(w, n) -> lng >= n && isPrefixOf u w) tbl
cmdsFromString :: String -> [(String, Int)] cmdsFromString s =
let go w@(x:_) (xs, n) | isDigit x = (xs, read w :: Int) | otherwise = ((toUpper <$> w, n) : xs, 0) in fst $ foldr go ([], 0) (words s)
-- TESTS -------------------------------------------------- table :: [(String, Int)] table =
cmdsFromString "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"
main :: IO () main = do
let unAbbrev = withExpansions table print $ unAbbrev "riG rePEAT copies put mo rest types fup. 6 poweRin" print $ unAbbrev ""</lang>
- Output:
"RIGHT REPEAT *error PUT MOVE RESTORE *error *error *error POWERINPUT" ""
J
Warning: http://rosettacode.org/wiki/Abbreviations,_easy#J uses this code without duplication. Must provide expand with the same signature. x is the command table string, y is the user sentence. <lang J> range=:<.+[:i.>.-<. assert 2 3 4 -: 2 range 5 assert (i.0) -: 3 range 3
abbreviate =: 3 :0
NB. input is the abbreviation table NB. output are the valid abbreviations y =. toupper CRLF -.~ y y =. ;: y y =. (([: (,3":#)L:_1 {)`[`]}~ ([: I. [: -. {.@e.&Num_j_&>)) y y =. [&.:(;:inv) y y =. _2 ({. , <./@:".&.>@:{:)\ y y =. (<@] , ((range #) <@{."0 _ ]))&.>~/"1 y ambiguities =. ;#~1<[:+/e. (([: ~. {.@:[ , -.)L:_1 <@:ambiguities) y
) assert ('ABC A AB' ,&(<@;:) 'ABCDE ABCD') -: abbreviate 'abc 1 abcde 3' assert ('ABC A AB' ,&(<@;:) 'ABCDE') -: abbreviate 'abc 1 abcde'
expand =: dyad define
a =. abbreviate x words =. <;._2 ' ' ,~ deb toupper y interval_index =. <: +/\ #&> a a =. a , <,<'*error*' ;:inv {.&> (interval_index I.(; a )i."_ 0 words){ a
) </lang>
command_table =: 0 :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 ) user_words =: 'riG rePEAT copies put mo rest types fup. 6 poweRin' command_table expand user_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; }, 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();
})();</lang>
- Output:
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Julia
<lang julia> 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")
</lang>
- Output:
riG rePEAT copies put mo rest types fup. 6 poweRin RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Kotlin
<lang kotlin>import java.util.Locale
private const 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 "
private data class Command(val name: String, val minLen: Int)
private fun parse(commandList: String): List<Command> {
val commands = mutableListOf<Command>() val fields = commandList.trim().split(" ") var i = 0 while (i < fields.size) { val name = fields[i++] var minLen = name.length if (i < fields.size) { val num = fields[i].toIntOrNull() if (num != null && num in 1..minLen) { minLen = num i++ } } commands.add(Command(name, minLen)) } return commands
}
private fun get(commands: List<Command>, word: String): String? {
for ((name, minLen) in commands) { if (word.length in minLen..name.length && name.startsWith(word, true)) { return name.toUpperCase(Locale.ROOT) } } return null
}
fun main(args: Array<String>) {
val commands = parse(table) val sentence = "riG rePEAT copies put mo rest types fup. 6 poweRin" val words = sentence.trim().split(" ")
val results = words.map { word -> get(commands, word) ?: "*error*" }
val paddedUserWords = words.mapIndexed { i, word -> word.padEnd(results[i].length) } println("user words: ${paddedUserWords.joinToString(" ")}") println("full words: ${results.joinToString(" ")}")
} </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
OCaml
<lang ocaml>open String
let table_as_string =
"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"</lang>
The interesting part below is the compare function. We'll use it when storing our entries ( {"add";1}, {"alter";3}, etc ) and when looking for a word, giving an abbreviation. For the former, a simple comparison function on strings is enough, but for the latter, we need to ensure that the word corresponds to the abbreviation (the abbreviation is a substring of the word and the abbreviation length is sufficient). <lang ocaml>module Entry = struct
type t = { word : string ; min : int } let compare e1 e2 = let n1 = length e1.word in if n1 < e2.min || n1 > length e2.word then compare e1.word e2.word else compare (sub e1.word 0 n1) (sub e2.word 0 n1)
end
module Table = Set.Make(Entry)</lang>
The few below functions are used to build the table from the string at the beginning. <lang ocaml>let clean_strings strs =
List.filter (fun w -> w <> "" && w <> " ") strs
let rec split = function
| [] -> Table.empty
| [w] -> Table.singleton { word=w ; min=length w }
| w::x::t -> try let m = int_of_string x in Table.add { word=uppercase_ascii w ; min=m } (split t) with Failure _ -> let m = length w in Table.add { word=uppercase_ascii w ; min=m } (split (x::t))
let make_table table_as_string =
split_on_char ' ' table_as_string |> clean_strings |> split</lang>
Finally, here is the function looking for a word : <lang ocaml>let abbrev (table:Table.t) (w:string) : string =
let w = uppercase_ascii w in try let e = Table.find { word=w ; min=length w } table in e.word with Not_found -> "*error*"
let rec check (table:Table.t) (inputs:string list) : unit =
List.map (abbrev table) inputs |> List.iter (Printf.printf "%s "); print_newline ()
let main =
let table = make_table table_as_string in let inputs = ["riG";"rePEAT";"copies";"put";"mo";"rest";"types";"fup.";"6";"poweRin"] in check table inputs; exit 0</lang>
- Output:
Input: riG rePEAT copies put mo rest types fup. 6 poweRin Output: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Perl
<lang perl>@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";</lang>
- 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
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.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;
}</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:
Phix
<lang Phix>constant abbrtxt = """
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
""", input = "riG rePEAT copies put mo rest types fup. 6 poweRin"
function set_min_lengths(sequence a) -- -- set default lengths and apply min lengths if present, eg ..."macro","merge","2",... -- ==> {.."macro",5}} ==> {.."macro",5},{"merge",5}} ==> {.."macro",5},{"merge",2}} -- ie both macro and merge get a default min length of 5, but the min length of merge -- is overwritten when the "2" is processed. --
sequence res = {} for i=1 to length(a) do string ai = a[i] if length(ai)=1 and ai>="1" and ai<="9" then res[$][2] = ai[1]-'0' else res = append(res,{ai,length(ai)}) end if end for return res
end function
constant abbrevs = set_min_lengths(split(substitute(abbrtxt,"\n"," "),no_empty:=true)) constant inputs = split(input,no_empty:=true)
for i=1 to length(inputs) do
string ii = inputs[i], res = "*error*" for j=1 to length(abbrevs) do {string aj, integer l} = abbrevs[j] if length(ii)>=l and match(ii,aj,case_insensitive:=true)==1 then res = upper(aj) exit end if end for puts(1,res&" ")
end for puts(1,"\n")</lang>
- Output:
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Python
Procedural
<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"
Composition of pure functions
<lang python>Simple abbreviations
from functools import reduce import re
- withExpansions :: [(String, Int)] -> String -> String
def withExpansions(table):
A string in which all words are either expanded (if they match abbreviations in a supplied table), or replaced with an '*error*' string. return lambda s: unwords(map( expanded(table), words(s) ))
- expanded :: [(String, Int)] -> String -> String
def expanded(table):
An abbreviation (or error string) for a given word, based on a table of full strings and minimum abbreviation lengths. def expand(k): u = k.upper() lng = len(k)
def p(wn): w, n = wn return w.startswith(u) and lng >= n return find(p)(table) if k else Just((, 0))
return lambda s: maybe('*error*')(fst)(expand(s))
- cmdsFromString :: String -> [(String, Int)]
def cmdsFromString(s):
A simple rule-base consisting of a list of tuples [( upper case expansion string, minimum character count of abbreviation )], obtained by a parse of single input string. def go(a, x): xs, n = a return (xs, int(x)) if x.isdigit() else ( ([(x.upper(), n)] + xs, 0) ) return fst(reduce( go, reversed(re.split(r'\s+', s)), ([], 0) ))
- TEST -------------------------------------------------
def main():
Expand any abbreviations in a string.
# table :: [(String, Int)] table = cmdsFromString( 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 )
# tests :: [String] tests = [ 'riG rePEAT copies put mo rest types fup. 6 poweRin', ]
print( fTable(__doc__ + ':\n')(lambda s: "'" + s + "'")( lambda s: "\n\t'" + s + "'" )(withExpansions(table))(tests) )
- GENERIC -------------------------------------------------
- compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
def compose(g):
Right to left function composition. return lambda f: lambda x: g(f(x))
- Just :: a -> Maybe a
def Just(x):
Constructor for an inhabited Maybe (option type) value. return {'type': 'Maybe', 'Nothing': False, 'Just': x}
- Nothing :: Maybe a
def Nothing():
Constructor for an empty Maybe (option type) value. return {'type': 'Maybe', 'Nothing': True}
- find :: (a -> Bool) -> [a] -> Maybe a
def find(p):
Just the first element in the list that matches p, or Nothing if no elements match. def go(xs): for x in xs: if p(x): return Just(x) return Nothing() return lambda xs: go(xs)
- fst :: (a, b) -> a
def fst(tpl):
First member of a pair. return tpl[0]
- fTable :: String -> (a -> String)
- -> (b -> String) -> (a -> b) -> [a] -> String
def fTable(s):
Heading -> x display function -> fx display function -> f -> value list -> tabular string. def go(xShow, fxShow, f, xs): w = max(map(compose(len)(xShow), xs)) return s + '\n' + '\n'.join([ xShow(x).rjust(w, ' ') + (' -> ') + fxShow(f(x)) for x in xs ]) return lambda xShow: lambda fxShow: lambda f: lambda xs: go( xShow, fxShow, f, xs )
- maybe :: b -> (a -> b) -> Maybe a -> b
def maybe(v):
Either the default value v, if m is Nothing, or the application of f to x, where m is Just(x). return lambda f: lambda m: v if m.get('Nothing') else ( f(m.get('Just')) )
- unwords :: [String] -> String
def unwords(xs):
A space-separated string derived from a list of words. return ' '.join(xs)
- words :: String -> [String]
def words(s):
A list of words delimited by characters representing white space. return re.split(r'\s+', s)
- MAIN ---
if __name__ == '__main__':
main()</lang>
- Output:
Simple abbreviations: 'riG rePEAT copies put mo rest types fup. 6 poweRin' -> '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
TCL
<lang tcl> proc appendCmd {word} {
# Procedure to append the correct command from the global list ::cmds # for the word given as parameter to the global list ::result. # If a matching word has been found and appended to ::result, this procedure # behaves like a "continue" statement, causing the loop containing it to # jump over the rest of the body. set candidates [lsearch -inline -all -nocase -glob $::cmds "${word}*"] foreach cand $candidates { if {[string length $word] >= $::minLen($cand)} { lappend ::result [string toupper $cand] return -code continue } }
}
set cmds {Add ALTer BAckup Bottom CAppend Change SCHANGE CInsert CLAst COMPress COpy
COUnt COVerlay CURsor DELete CDelete Down DUPlicate Xedit EXPand EXTract Find NFind NFINDUp NFUp CFind FINdup FUp FOrward GET Help HEXType Input POWerinput Join SPlit SPLTJOIN LOAD Locate CLocate LOWercase UPPercase LPrefix MACRO MErge MODify MOve MSG Next Overlay PARSE PREServe PURge PUT PUTD Query QUIT READ RECover REFRESH RENum REPeat Replace CReplace RESet RESTore RGTLEFT RIght LEft SAVE SET SHift SI SORT SOS STAck STATus TOP TRAnsfer Type Up}
- Find the minimum lengths necessary for each command.
foreach c $cmds {
regexp {^[A-Z]+} $c match set minLen($c) [string length $match]
}
set words {riG rePEAT copies put mo rest types fup. 6 poweRin} set result {}
foreach w $words {
appendCmd $w lappend result *error*
}
puts "user words: $words" puts $result </lang>
- Output:
./abbreviations_simple.tcluser words: riG rePEAT copies put mo rest types fup. 6 poweRin RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
VBA
<lang vb>Private Function ValidateUserWords(userstring As String) As String
Dim s As String Dim user_words() As String Dim command_table As Scripting.Dictionary Set command_table = New Scripting.Dictionary Dim abbreviations As Scripting.Dictionary Set abbreviations = New Scripting.Dictionary abbreviations.CompareMode = TextCompare Dim commandtable() As String Dim commands As String s = s & "add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 " s = s & "compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate " s = s & "3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 " s = s & "forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load " s = s & "locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 " s = s & "msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 " s = s & "refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left " s = s & "2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1 " commandtable = Split(s, " ") Dim i As Integer, word As Variant, number As Integer For i = LBound(commandtable) To UBound(commandtable) word = commandtable(i) If Len(word) > 0 Then i = i + 1 Do While Len(commandtable(i)) = 0: i = i + 1: Loop number = Val(commandtable(i)) If number > 0 Then command_table.Add Key:=word, Item:=number Else command_table.Add Key:=word, Item:=Len(word) i = i - 1 End If End If Next i For Each word In command_table For i = command_table(word) To Len(word) On Error Resume Next abbreviations.Add Key:=Left(word, i), Item:=UCase(word) Next i Next word user_words() = Split(userstring, " ") For Each word In user_words If Len(word) > 0 Then If abbreviations.exists(word) Then commands = commands & abbreviations(word) & " " Else commands = commands & "*error* " End If End If Next word ValidateUserWords = commands
End Function Public Sub program()
Dim guserstring As String guserstring = "riG rePEAT copies put mo rest types fup. 6 poweRin" Debug.Print "user words:", guserstring Debug.Print "full words:", ValidateUserWords(guserstring)
End Sub </lang>
- Output:
user words: riG rePEAT copies put mo rest types fup. 6 poweRinfull 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