The Mod Out System

June 23, 2009

Dan Schoch provides this PL/SQL function:

-- local variables
token_pointer pls_integer := 1 ;

-- get a token from a string (either integer, - or comma) -------- get_token
function get_token( parm_str in varchar2) return varchar2 is
-- returns Integer as varchar2, (C)omma, (D)ash, (O)ther
    my_pointer pls_integer := 0 ;
    num pls_integer := 0 ;
begin
    case when substr(parm_str,token_pointer,1) = ','
         then token_pointer := token_pointer + 1 ;
              return 'C' ;
         when substr(parm_str,token_pointer,1) = '-'
         then token_pointer := token_pointer + 1 ;
              return 'D' ;
         else while (token_pointer <= my_pointer+length(parm_str)
                and substr(parm_str,token_pointer+my_pointer,1) not in (',','-'))
              loop num := num*10 + to_number(substr(parm_str,token_pointer+my_pointer,1)) ;
                   my_pointer := my_pointer + 1 ;
              end loop ;
              token_pointer := token_pointer + my_pointer ;
              return to_char(num,'fm999999999') ;
    end case ;
exception when others then return 'O' ;
end get_token ;

-- parses string to list of numbers ----------------------------- parse_nums
function parse_nums(parm_str in varchar2) return varchar2 is
    ret_str varchar2(4000) := null ;
    cur_token varchar2(12) := null ;
    last_token varchar2(12) := 'C' ;
    start_range pls_integer := null ;

begin
    token_pointer := 1 ;
    while (token_pointer <= length(parm_str))
    loop
        cur_token := get_token(parm_str) ;
        case
            when cur_token not in ('C','D','O') and last_token = 'C'
            then ret_str := ret_str||cur_token||',' ;
            when cur_token not in ('C','D','O') and last_token = 'D'
            then for i in start_range+1..to_number(cur_token)
                 loop ret_str := ret_str||to_char(i,'fm999999999')||',' ;
                 end loop ;
            when cur_token = 'C' and last_token not in ('C','D','O')
            then null ;
            when cur_token = 'D' and last_token not in ('C','D','O')
            then start_range := to_number(last_token) ;
            else p.l('error at position '||to_char(token_pointer,'fm999,999')) ;
        end case ;
        last_token := cur_token ;
    end loop ;
    return substr(ret_str,1,length(ret_str)-1) ;
exception when others then return 'error at position '||to_char(token_pointer,'fm999,999') ;
end parse_nums ;

A Scheme solution is shorter and clearer:

(define (make-range s)
  (if (not (member #\- (string->list s))) s
    (let ((ab (string-split #\- s)))
      (string-join #\,
        (map number->string
          (range (string->number (car ab))
                 (+ (string->number (cadr ab)) 1)))))))

(define (mod-out str)
  (string-join #\, (map make-range (string-split #\, str))))

Make-range expands a range, mod-out calls make-range for each entry in the list. String-split, string-join and range come from the Standard Prelude. It looks like this:

> (mod-out "1-6,9,13-19")
"1,2,3,4,5,6,9,13,14,15,16,17,18,19"

You can run this program at http://programmingpraxis.codepad.org/5rArlKwi.

About these ads

Pages: 1 2

7 Responses to “The Mod Out System”

  1. […] Praxis – The Mod Out System By Remco Niemeijer Today‚Äôs Programming Praxis problem is an easy one: all we have to do is convert a string that contains […]

  2. Remco Niemeijer said

    My Haskell solution (see http://bonsaicode.wordpress.com/2009/06/23/programming-praxis-the-mod-out-system/ for a version with comments):

    import Data.List.Split
    
    modOut :: String -> [Int]
    modOut = concatMap ((\(a:b:_) -> [a..b]) .
        cycle . map read . sepBy "-") . sepBy ","
    
    main :: IO ()
    main = print $ modOut "1-6,9,13-19"
    
  3. liesen said

    In Scala:

    object ModOutSystem {
    def modOut(s: String) =
    s split “,” flatMap { _ split “-” match {
    case Array(single) => List(single.toInt)
    case Array(from, to) => List.range(from.toInt, to.toInt + 1)
    } }

    def main(args: Array[String]) = println(modOut(“1-6,9,13-19″).deepToString) // Array(1, 2, 3, 4, 5, 6, 9, 13, 14, 15, 16, 17, 18, 19)
    }

  4. Mary said

    Pretty nice post. I just came by your blog and wanted to say
    that I have really enjoyed reading your posts. Any way
    I’ll be subscribing to your feed and I hope you post again soon!

  5. Barry said

    In Python2.x:

    def combineranges(a):
    b = [[int(j) for j in i.split(“-“)] for i in a.split(“,”)]
    c = []
    [c.extend(range(b[i][0],b[i][len(b[i])-1]+1)) for i in range(len(b))]
    return c

    print combineranges(“1-6,9,13-19″)

  6. Connochaetes said

    In Ruby:

    def expand(s)
    s.inject {|x,y| x..y}.to_a
    end

    def mod_out(mod_string)
    mod_string.split(“,”).collect {|x| x.split(“-“)}.collect {|x| expand(x)}.join(“,”)
    end

    puts mod_out “1-6,9,13-19″

  7. A quick hack in Common Lisp.

    (defun mod-out (ranges)
    (flatten
    (loop for range in ranges
    collect (if (consp range)
    (loop for i from (car range) to (cdr range) collect i)
    range))))

    ;; usage
    (mod-out ‘((1 . 6) 9 (13 . 19)))

    Take FLATTEN from your favorite utility library or write it yourself.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 644 other followers

%d bloggers like this: