Abbreviations, simple

Revision as of 17:02, 30 May 2018 by PureFox (talk | contribs) (Added Go)

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.

Abbreviations, simple is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.


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

Translation of: Kotlin

<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

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

Works with: Rakudo version 2017.08

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*' }

  1. 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: 

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..."

  1. <<<

"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());

  1. <<<

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