A solution to Ruby Quiz #14 in literate Haskell:
LCD Numbers
===========
Problem
-------
[original source](http://rubyquiz.com/quiz14.html)
This week's quiz is to write a program that displays LCD style numbers
at adjustable sizes.
The digits to be displayed will be passed as an argument to the
program. Size should be controlled with the command-line option -s
follow up by a positive integer. The default value for -s is 2.
For example, if your program is called with:
$ lcd.rb 012345
The correct display is:
-- -- -- --
| | | | | | | |
| | | | | | | |
-- -- -- --
| | | | | | |
| | | | | | |
-- -- -- --
And for:
$ lcd.rb -s 1 6789
Your program should print:
- - - -
| | | | | |
- - -
| | | | | |
- - -
Note the single column of space between digits in both examples. For
other values of -s, simply lengthen the - and | bars.
Solution
--------
Module declaration and imports:
> module Main where
>
> import Data.Char (digitToInt)
> import Data.List (intersperse)
> import System.Console.GetOpt
> import System.Environment (getArgs)
First we define the numbers at size 1:
> n0 = [ " - "
> , "| |"
> , " "
> , "| |"
> , " - "
> ]
>
> n1 = [ " "
> , " |"
> , " "
> , " |"
> , " "
> ]
>
> n2 = [ " - "
> , " |"
> , " - "
> , "| "
> , " - "
> ]
>
> n3 = [ " - "
> , " |"
> , " - "
> , " |"
> , " - "
> ]
>
> n4 = [ " "
> , "| |"
> , " - "
> , " |"
> , " "
> ]
>
> n5 = [ " - "
> , "| "
> , " - "
> , " |"
> , " - "
> ]
>
> n6 = [ " - "
> , "| "
> , " - "
> , "| |"
> , " - "
> ]
>
> n7 = [ " - "
> , " |"
> , " "
> , " |"
> , " "
> ]
>
> n8 = [ " - "
> , "| |"
> , " - "
> , "| |"
> , " - "
> ]
>
> n9 = [ " - "
> , "| |"
> , " - "
> , " |"
> , " - "
> ]
>
Put the numbers in a list:
> numbers = [n0,n1,n2,n3,n4,n5,n6,n7,n8,n9]
Horizontal scaling function, given a string replicate the second
character n times:
> hscale n cs = head cs : replicate n (cs!!1) ++ [last cs]
Vertical scaling function, repeat the second and fourth row n times:
> vscale n css = head css : replicate n cs1 ++ [cs2] ++ replicate n cs3 ++ [cs4]
> where cs1 = css !! 1
> cs2 = css !! 2
> cs3 = css !! 3
> cs4 = last css
Scale function; note this function scales a single number:
> scale n = vscale n . map (hscale n)
Function that converts a list of numbers to a string of LCD numbers:
> lcd n = concat .
> intersperse "\n" .
> foldr1 (zipWith (++)) .
> intersperse (replicate (3 + 2*n) " ") .
> map (scale n . (numbers !!))
`main` function:
> main = do
> args <- getArgs
> let (n, digits) = parseArgs args
> putStrLn $ lcd n $ map digitToInt digits
Command-line argument parsing:
> data Flag = Scale Int
> deriving Eq
>
> options = [Option "s" [] (ReqArg (Scale . read) "") ""]
>
> parseArgs args =
> case parse args of
> (_, [], _) -> error "Usage: lcd [-s n] digits"
> ([], digits, []) -> (2, head digits)
> ([Scale n], digits, []) -> (n, head digits)
> (_, _, _) -> error "Usage: lcd [-s n] digits"
> where
> parse = getOpt RequireOrder options
And for a short, if not as sweet perl version:
#!/usr/bin/env perl use strict; use warnings; my ( undef, $scale, $input ) = @ARGV == 3 ? @ARGV : ( '', '2', @ARGV ); my @digits; push( @digits, $1) while $input =~ m/(\d)/g; my $empty = ' '; my $line = ' - '; my $sides = '| |'; my $right = ' |'; my $left = '| '; my @d; $d[0] = [$line, $sides, $empty, $sides, $line ]; $d[1] = [$empty, $right, $empty, $right, $empty ]; $d[2] = [$line, $right, $line, $left, $line ]; $d[3] = [$line, $right, $line, $right, $line ]; $d[4] = [$empty, $sides, $line, $right, $empty ]; $d[5] = [$line, $left, $line, $right, $line ]; $d[6] = [$line, $left, $line, $sides, $line ]; $d[7] = [$line, $right, $empty, $right, $empty ]; $d[8] = [$line, $sides, $line, $sides, $line ]; $d[9] = [$line, $sides, $line, $right, $line ]; for my $line (0..4) { for (1..($line % 2 == 1 ? $scale : 1)) { for my $digit ( @digits ) { $d[$digit][$line] =~ m/^(.)(.)(.)$/; print $1 . ( $2 x $scale) . $3 . ' '; } print "\n"; } }Namens marius in bash en awk :)
#!/bin/bash DIGITS="" SCALE="2" function parse_args { while getopts "s:" flag do SCALE="$OPTARG" done shift $(($OPTIND-1)) DIGITS="$1" } SPRITE_LINE0=".-.....-..-.....-..-..-..-..-." SPRITE_LINE1="|.|..|..|..||.||..|....||.||.|" SPRITE_LINE2=".......-..-..-..-..-.....-..-." SPRITE_LINE3="|.|..||....|..|..||.|..||.|..|" SPRITE_LINE4=".-.....-..-.....-..-.....-..-." NEWLINE_SUBSITUTE="L" READ_DELIM="X" function digit_to_sprite { local DIGIT="$*" local SPRITE_TEMPLATE_OFFSET=$(($DIGIT*3)) for SPRITE_LINE_NR in $(seq 0 4); do local VAR_SELECTOR="SPRITE_LINE$SPRITE_LINE_NR" echo -n ${!VAR_SELECTOR:$SPRITE_TEMPLATE_OFFSET:3}$NEWLINE_SUBSITUTE done echo } function digits_to_sprites { local DIGITS="$*" DIGITS="$(echo $DIGITS | sed 's/\(.\)/\1 /g')" for DIGIT in $DIGITS ; do digit_to_sprite $DIGIT done } function calculate_scale { local SPRITES=$1 local FIRST_SPRITE=$(echo $SPRITES | head -1) local TOTAL_LINES=$(echo -n $FIRST_SPRITE | tr $NEWLINE_SUBSITUTE \\n | wc -l) echo $((($TOTAL_LINES-3)/2)) } function render_sprites { local SPRITES="" read -d $READ_DELIM SPRITES local SPRITE_SCALE=$(calculate_scale $SPRITES) for LINE_NR in $(seq 1 $((($SPRITE_SCALE * 2) + 3))); do local TOTAL_LINE="" for SPRITE in $SPRITES; do local PIECE=$(echo -n $SPRITE | cut -d $NEWLINE_SUBSITUTE -f $LINE_NR) TOTAL_LINE="$TOTAL_LINE$PIECE " done echo $TOTAL_LINE | tr '.' ' ' done } function scale_sprites { local SCALE=$1 local SPRITES="" read -d $READ_DELIM SPRITES local VSCALE_AWK=" /([|][.][.])|([.][.][|])|([|][.][|])/ { for (i = 1; i < scale; i++) print \$0 } BEGIN { RS=\"$NEWLINE_SUBSITUTE\" ORS=\"$NEWLINE_SUBSITUTE\" } { print \$0 } " local HSCALE_AWK=" BEGIN { RS=\"$NEWLINE_SUBSITUTE\" ORS=\"$NEWLINE_SUBSITUTE\" FS=\"\" OFS=\"\" } { line = \$1 for (i = 1; i <= scale; i++) line = line \$2 line = line \$3 print line } " AWK_VARIABLES="scale=$SCALE" for SPRITE in $SPRITES; do echo -n "$SPRITE" \ | awk "$VSCALE_AWK" $AWK_VARIABLES \ | awk "$HSCALE_AWK" $AWK_VARIABLES echo done } parse_args $* digits_to_sprites $DIGITS | scale_sprites $SCALE | render_sprites