Display a tally (in varying bases)

16

1

Tallying is a simple counting system that works in base 5. There are various different tallying systems used around the world, but the one that is used in most English-speaking countries is perhaps the most simple - count units by marking vertical lines, then for each 5th mark put a horizontal line through the previous collection of four. This clusters the tally marks in groups of 5 (and makes them easier to quickly count).

You are going to write a program that displays tally marks up to a given value. But, tallying in only base 5 is boring! Therefore, your program should also be able to display tallies in different bases.

Input

The input will be either one or two non-negative integer values separated by a comma (e.g. 9 or 8,4). The first number is the value that should be displayed by the tally. The second value is the base of the tally. If the second value is not given, use base 5.

Output

The output will be the inputted value represented as ASCII art tally marks. Here are some examples you can test your program against - your output should match them exactly!

Input: 12 or 12,5

 | | | |   | | | |   | |
-+-+-+-+- -+-+-+-+-  | |
 | | | |   | | | |   | |

Input: 7,3

 | |   | |   |
-+-+- -+-+-  |
 | |   | |   |

Input: 4,2

 |   |
-+- -+-
 |   |

Input: 6,1 or 6,10 (notice the leading spaces)

 | | | | | |
 | | | | | |
 | | | | | |

Note also that base 1 is intended to be inconsistent - only vertical lines should be used.

If either of the inputted values is 0, there should be no output whatsoever (and your program should end gracefully).

Rules

  • This is , so the shortest correct implementation (in bytes) wins.
  • Input/output can be in any suitable medium (e.g. stdin/stdout, file...).
  • Input can be in the form of multiple command-line arguments or separated by spaces, etc. if it is more suitable for your target language.
  • Trailing newlines are allowed in the output. Trailing spaces are not. This rule only applies when there is an output (i.e. not when the inputted value is 0).
  • Your code must default to base 5 when no base is input.

Sean Latham

Posted 2014-08-20T12:00:46.743

Reputation: 1 423

3Shouldn't the output of 6,1 look more like -+- -+- -+- -+- -+- -+-? – Peter Taylor – 2014-08-20T12:38:50.890

3If you state "The input will be either one or two positive integer values separated by a comma (e.g. 9 or 8,4)." then we should be able to take that as a given, and not have to worry about "Your program should be robust - you should validate the input..." beyond handling one or two numbers. – AndoDaan – 2014-08-20T12:42:32.017

1@PeterTaylor -+- would represent two, because there's a vertical line and a horizontal score through it. Base 1 would only have vertical lines. @AndoDaan ammended. – Sean Latham – 2014-08-20T12:45:30.577

Ok, --- --- --- --- --- --- then. For consistency with the other bases you should be putting a horizontal strike through b-1 vertical lines. If it's intended to be inconsistent you should state that explicitly. – Peter Taylor – 2014-08-20T12:54:32.453

I've done that. Sorry, I thought it was implied. – Sean Latham – 2014-08-20T13:03:38.640

is there any leeway in the input format (comma-separated values are much less helpful than two separate command-line arguments, or even just space-separated standard input)? – None – 2014-08-20T13:27:33.343

Yes, I'll add this to the rules. – Sean Latham – 2014-08-20T13:40:06.453

Are trailing newlines allowed? – None – 2014-08-20T14:03:54.640

Yes, added. I need to find more ways of surpassing the 15 character minimum... – Sean Latham – 2014-08-20T14:10:27.620

But are trailing newlines allowed if n==0? Or does the "n=0->no-output" rule overrule the trailing-newline rule? – Falko – 2014-08-20T14:12:06.127

The no output rule overrides it. Ideally your program will spot that n = 0 and exit before any printing is done (or just skip it altogether). – Sean Latham – 2014-08-20T14:14:40.003

Is there a maximum value for the numbers? Can I assume they're less than two billion? – Ypnypn – 2014-08-20T15:57:52.053

I said "positive integer", so I guess the maximum depends on your implementation. I'm not evil enough to want to test any inputs that high though :^) – Sean Latham – 2014-08-20T16:02:18.497

@ipi I was about to abuse a mistake you made when I decided I'd better correct your post instead. Zero is not "positive" yet it is a valid input, so I changed it to say "non-negative". – Kyle McCormick – 2014-08-21T00:59:30.693

With regard to the "no trailing spaces" rule, does this forbid trailing spaces at the end each of the 3 lines of output, or am I missing the point completely? – VisualMelon – 2014-08-21T11:32:40.343

If input is two numbers separated with a comma, like 7,2, can I implement this as receiving input between the parentheses of a function call like f(7,2)? – FUZxxl – 2014-08-21T14:13:26.960

Can we assume 2¹⁵ - 1 as an upper limit for the numbers you test? – FUZxxl – 2014-08-21T14:22:58.637

Answers

4

CJam 103 85 72 characters

Try it at http://cjam.aditsu.net/.

original

q","/(i:A\_,{~i}{;5}?:B_@*{(_{" |"*S"l"++AB/*AB%}{;MA}?\:J" |l""-+ "er\" |"*N+_J\+@2$+@J\+++"l"Ser}{;}?

Works by defining one set of tallys with spaces, lines and an l for spaces that should remain spaces. Then takes advantage of er (tranliteration) function to make the second line. Most inefficient part is dealing with the 1 and 0 special cases. Will edit as I improve it. Tip I took too long to realize: as the second input being 1 is the same as infinity or the first input +1, redefining it when it is equal to 1 saves alot of work.

most improved so far with comma delimited

l",":G/(i:A\_5a?~i:B_@*{(_" |":K*\{SG++AB/*AB%}{A}?\_KG+"-+ "er[\GSer_@@M]\K*N+*}{;}?

most improved so far with space delimited input

Naturally as CJam is really designed for space delimited input. Placing the input at 20 3 instead of 20,3 is a huge benefit.

ri:Aq_5s?S-i(_)A)?:B*{B(" |":K*STs++ABmd@@*_K"-+"er[\_@@M]\K*N+*TsSer}M?

kaine

Posted 2014-08-20T12:00:46.743

Reputation: 536

5

Python 2 - 111 108 119 144 140 136 135 134 - Try it

Ok, let's give it a try:

i=input()
n,b=[(i,5),i][i>[]]
o=b-1
a=[n,n%b][b>1]*' |'
m=(b>1)*n/b
s=(' |'*o+'  ')*m+a
print(s+'\n'+('-+'*o+'- ')*m+a+'\n'+s)*(b*n>0)

Edit: I've overlooked that there should be no output if n==0 or b==0. This costs me 11 characters. :(

Edit: Ok, after fixing the second issue mentioned in the comments my solution basically converged to the one from BeetDemGuise.

Falko

Posted 2014-08-20T12:00:46.743

Reputation: 5 307

This prints newlines when either of the inputs (or both) are zero which according the challenge is undesired. Also, what if only one number is input into the program? – BeetDemGuise – 2014-08-20T13:40:31.640

1This fails when the second value is omitted (b should be 5 in this case). I'll make it more clear in the question. Edit: oh, never mind, you fixed it just as I made this comment! – Sean Latham – 2014-08-20T13:44:43.763

Which Python is this? – Beta Decay – 2014-08-20T14:46:03.097

It's Python 2.7.8. - Oh, there was a tiny mistake at the very end... – Falko – 2014-08-20T14:48:50.987

1If it's Python 2.x couldn't you save one more character by using n/binstead of n//b? – Emil – 2014-08-20T15:31:29.650

Hey, you're right! I always forget that Python does integer division by default, since the Spyder command line does not: 3/2 yields 1.5. – Falko – 2014-08-20T15:34:54.997

If you try this with large numbers then the result looks sloppy. Example

– GeoffWilson – 2014-08-21T10:08:57.083

I believe this answer will leave trailing spaces for input "5". – VisualMelon – 2014-08-21T11:08:56.107

@VisualMelon: No, I don't think so. You can try it here.

– Falko – 2014-08-21T11:13:25.540

@G_Wilson: That behavior was not clearly specified and I guess every answer here has its problems for large numbers. For a sufficiently wide terminal window it'll work. ;) – Falko – 2014-08-21T11:13:50.833

@Falko IDEOne refuses to work for me, but I asked a friend to test it was printing 5 trailing spaces for input five (2 on the first line, one on the next, two on the last line (padded to the 2*n char, as such)). I may be mis-interpreted what the OP meant by trailing spaces, I'll ask him to clarify in a comment. – VisualMelon – 2014-08-21T11:30:54.920

5

Bash, 239 228 199 189 188

Here's my attempt, it could be golfed a lot.

Note: the second line does not subtract 5 from 2, it sets a default value if $2 is empty!

n=$1
b=${2-5}
((n<1&b<1))&&exit
while ((n>b&b>1));do
n=$[n-b]
x+=\
y+=-
for i in `seq $[b-1]`;{
x+='| '
y+=+-
}
x+=\
y+=\
done
for j in `seq $n`;{
x+=' |'
y+=' |'
}
echo -e "$x\n$y\n$x"

user16402

Posted 2014-08-20T12:00:46.743

Reputation:

Does {1..$n} work instead of \seq $n``? – FUZxxl – 2014-08-21T14:27:22.473

@FUZxxl unfortunately not, h=8;echo {1..$h} outputs {1..8} – None – 2014-08-21T16:58:02.617

That's not good. – FUZxxl – 2014-08-21T17:08:24.430

3

Python - 171 143

i=input();t,b=i if[0]<i else(i,5);b=[b,t+1][b==1];l,d,m,o=' |',t/b,t%b,b-1;r=(l*o+'  ')*d+l*m
if t*b>0:print r,'\n',('-+'*o+'- ')*d+l*m,'\n',r

The program is pretty straight-forward:

  • Get the input and try to unpack into t,b. If that fails, simply assign the correct values.
  • If the base was 1, change its value to something that can handle all vertical lines easily (t+1).
  • Set some variables and create the bottom and top sections of the tallies.
  • Print out the tallies if both t and b are non-zero.

EDIT 1: Use the input function instead of raw_input after some playing around.

EDIT 2: Thanks to Falko for pointing out a small bug with my non-zero checking. Now our code is basically identical, less some variable names and some small logic.

EDIT 3: Thanks to how Python compares sequences and different types, we can compare i to a list to get a shorter version of our try...except block.

Here is the ungolfed version:

i=input()

# If True, `i` must be a list
if [0]<i:
    t,b=i
# Otherwise, we know its a number (since `list` comes after `int` lexicographically.)
else:
    b=5
    t=i

b = b if b==1 else t+1
l=' |'
d=t/b
m=t%b
o=b-1

r=(l*o+'  ')*d+l*m
if t and b:
    print r,'\n',('-+'*o+'- ')*d+l*m,'\n',r

BeetDemGuise

Posted 2014-08-20T12:00:46.743

Reputation: 442

I think t&b is False for 10,5. Otherwise our solutions are converging! ;) – Falko – 2014-08-20T13:51:51.610

@Falko You are right on both counts! You know what they say about great minds. – BeetDemGuise – 2014-08-20T13:56:11.463

It'd be really great if we could find a short way to test if i is scalar or a list. Then we could drop the try ... except monster. – Falko – 2014-08-20T14:09:09.897

@Falko I think I found a 1byte-better check. A list is always greater than an int. Also, lists are compared in lexicographical order. So if we compare [0]<i this will always return False if i is a number and True if i is a list (with a non-zero first element). – BeetDemGuise – 2014-08-20T14:24:50.993

1Great! I further shortened your approach. Nice teamwork! :) – Falko – 2014-08-20T14:32:02.953

I have not been able to test this (can't get IDEOne to work and I don't have Python 2) but it would appear to me that this will print a trailing space in the 2nd line if it ends "- ", am I missing something? – VisualMelon – 2014-08-20T17:17:54.110

3

C - 193

I'm so sorry. Dealing with the special case for 1 was a bit of a bad hack so I guess this could be golfed more with a better approach. Also, this code includes a new line at the beginning of the output, so if this is not allowed, please let me know.

Of course, very ugly-looking defines always help :)

Character count includes only necessary spaces and new lines.

#define P printf(
#define L P" |")
#define A P"\n");for(i=0;i<a;)b==1?i++,L:i++&&i%b==0?P
i;
main(a,b)
{
    scanf("%d,%d",&a,&b)<2?b=5:!b?a=0:a;
    if(a){
    A"  "):L;
    A"- "):a%b&&i>a/b*b?L:P"-+");
    A"  "):L;}
}

Allbeert

Posted 2014-08-20T12:00:46.743

Reputation: 489

Your code seems to print newlines when one of the values is zero. This is explictly disallowed. Your solution is not conforming. – FUZxxl – 2014-08-21T15:54:57.673

@FUZxxl You're right, I missed that! This bad quick fix will have to do for now. I hope I have time soon to find a better way. – Allbeert – 2014-08-21T16:17:22.960

I'm pretty sure you can save a few characters by replacing printf with puts, and replacing return with a ternary operator. – millinon – 2014-08-23T23:51:11.700

@millinon The problem with puts is that it adds a new line every time :(. And for the ternary operator, it is not possible to add returns or fors inside them!. Your comment did give me the idea to save a few more characters very easily by removing the return though. Thanks! – Allbeert – 2014-08-26T20:38:38.077

3

Java, 343

class C{public static void main(String[]a){long n=new Long(a[0])+1,b=a.length>1?new Long(a[1]):5;if(b>0){if(b<2)b=(int)2e9;int i;for(i=1;i<n;i++)p(i%b>0?" |":"  ");p("\n");for(i=1;i<n-n%b;i++)p(i%b>0?"-+":"- ");if(n>b)p("- ");for(i=1;i<n%b;i++)p(" |");p("\n");for(i=1;i<n;i++)p(i%b>0?" |":"  ");}}static void p(String s){System.out.print(s);}}

Less golfed:

class C {
  public static void main(String[] a) {
    long n=new Long(a[0])+1, b=a.length>1 ? new Long(a[1]) : 5;
    if(b>0) {
      if(b<2) b=(int)2e9; // if the base is 1, pretend the base is 2 billion
      int i;
      for(i=1;i<n;i++) p(i%b>0 ? " |" : "  ");
      p("\n");
      for(i=1;i<n-n%b;i++) p(i%b>0 ? "-+" : "- ");
      if(n>b) p("- ");
      for(i=1;i<n%b;i++) p(" |");
      p("\n");
      for(i=1;i<n;i++) p(i%b>0 ? " |" : "  ");
    }
  }
  static void p(String s) {
    System.out.print(s);
  }
}

Ypnypn

Posted 2014-08-20T12:00:46.743

Reputation: 10 485

You can save a few by making i a long so you don't have to declare it separately. A few more by doing i++%b>0 in your loops instead of incrementing it separately (and i++<n%b in the third loop). Another one by using b=b<2?(int)2e9:b. – Geobits – 2014-08-21T13:29:12.147

3

Perl - 167 165 156

my($n,$b)=($ARGV[0]=~/(\d+)(?:,(\d+))?/,5);print$b>1?map{join(" ",($_ x($b-1))x int($1/$b)," | "x($1%$b))."\n"}(" | ","-+-"," | "):join" ",("---")x$1if$1*$b

ungolfed

my($n,$b) = ($ARGV[0] =~ /(\d+)(?:,(\d+))?/, 5);
print($b>1 ?
    map{ 
        join(" ",($_ x ($b-1)) x int($1/$b)," | " x ($1%$b))."\n"
    } (" | ","-+-"," | ") :
    join " ", ("---") x $1
) if $1 * $b

Fozi

Posted 2014-08-20T12:00:46.743

Reputation: 131

displays horizontal lines instead of vertical ones for base 1 :( – chinese perl goth – 2014-08-21T15:13:55.633

@chineseperlgoth yes, that's one of the requirements. Please read comments on Q. – Fozi – 2014-08-23T01:35:39.850

2

C# 271bytes

Not the shortest, I couldn't golf the input reading due to it needing to accept 0 as an input.

using C=System.Console;class P{static void Main(){var L=C.ReadLine().Split(',');int t=int.Parse(L[0]),f=L.Length>1?int.Parse(L[1]):5,r=3,i;for(var R="";t*f>0&r-->0;C.WriteLine(R.TrimEnd()))for(R="",i=0;i<t;R+=r==1&i++<t-t%f?(i%f<1?"- ":"-|"):i%f<1?"  ":" |")f+=f<2?t:0;}}

Formatted code:

using C=System.Console;

class P
{
    static void Main()
    {
        var L=C.ReadLine().Split(',');
        int t=int.Parse(L[0]),f=L.Length>1?int.Parse(L[1]):5,r=3,i;

        for(var R="";t*f>0&r-->0;C.WriteLine(R.TrimEnd()))
            for(R="",i=0;i<t;R+=r==1&i++<t-t%f?(i%f<1?"- ":"-|"):i%f<1?"  ":" |")
                f+=f<2?t:0;
    }
}

VisualMelon

Posted 2014-08-20T12:00:46.743

Reputation: 3 810

1

Lua - 219 203 bytes

I went for the make d copies of b copies of "|", then add r copies of "|" at the end. I feel like maybe I should have gone with the 'tally up' the "|"s to the string one at a time.

l=' |'s=string.rep _,_,a,b=io.read():find'(%d+)%D*(%d*)'b=tonumber(b)or 5 d=(a-a%b)/b f=b>1 and s(s(l,b-1)..'  ',d)g=b>1 and s(s('-+',b-1)..'- ',d)r=b>1 and a%b or a e=s(l,r)..'\n'print(f..e..g..e..f..e)

ungolfed:

l=' |'          --the base string
s=string.rep    --string.rep will be used a lot, so best shorten it

_,_,a,b=io.read():find'(%d+)%D*(%d*)' --reads a,b I'm probably way of the mark with this one

b=tonumber(b)or 5

d=(a-a%b)/b -- shorter than math.floor(a/b), d equal the vertical mark

f=b>1 and s(s(l,b-1)..'  ',d) or '' --creates d multiples of b multiples of "|" more or less
g=b>1 and s(s('-+',b-1)..'- ',d)or''--same as above but with the middle "-+-"

r=b>1 and a%b or a --idk maybe i should set r before d(a- a%b )/b

e=s(l,r)..'\n'  -- makes the remainder string, notice that if b==1  then e will output all the "|" 

print(f..e..g..e..f..e) -- here's where the real magic happens!

Sample:

c:\Programming\AnarchyGolfMine>lua test.lua
13,5
 | | | |   | | | |   | | |
-+-+-+-+- -+-+-+-+-  | | |
 | | | |   | | | |   | | |


c:\Programming\AnarchyGolfMine>lua test.lua
6,2
 |   |   |
-+- -+- -+-
 |   |   |


c:\Programming\AnarchyGolfMine>lua test.lua
18,1
 | | | | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | |

AndoDaan

Posted 2014-08-20T12:00:46.743

Reputation: 2 232

1Could you post a sligthly more readable and ungolfed version? Golfing in Lua looks interesting! – None – 2014-08-20T16:03:54.807

@Alessandro done. And thanks, it made find a couple things I missed. – AndoDaan – 2014-08-20T17:43:18.107

1

JavaScript (193)

This might be overly complex.

s=prompt().split(",");a=+s[0];b=s[1];b=b?+b:5;o=b>1;v=" | ";q=w="";for(g=~~(a/b),c=o?a-g:a;g+1;g--,q+=" ",w+=" ")for(i=o;c&&i<b;i++)c--,q+=v,w+=g&&o?"-+-":v;if(a&&b)console.log(q+'\n'+w+'\n'+q)

Commented version

s=prompt().split(",");
a=+s[0];
b=s[1];
b=b?+b:5;   // convert b to int and default to 5
o=b>1;      // special handling for b0 and b1
v=" | ";
q=w="";
// calculate number of lines and number of groups
for(g=~~(a/b),c=o?a-g:a;g+1;g--,q+=" ",w+=" ")
    for(i=o;c&&i<b;i++)
        c--,  // decrease line count
        q+=v,
        w+=g&&o?"-+-":v; // use " | " for last group and "-+-" for others
if(a&&b) // handle b0
    console.log(q+'\n'+w+'\n'+q)

Mika Lammi

Posted 2014-08-20T12:00:46.743

Reputation: 1 151

1

Python - 127 123 122

Just sneaking in with a slightly shorter python version.

edit: Fixed 0 not printing nothing and rejigger, ended up the same length

k=input()
k=i,j=((k,5),k)[k>[]]
for m in[' |','-+',' |']*all(k):
 print(m*(j-1)+m[0]+' ')*(i/j*(j>1))+' |'*(i%(j+(j<2)*i))

Bizangles

Posted 2014-08-20T12:00:46.743

Reputation: 121

0

Python 2, 134 126 123 114 bytes

lambda i,j=5,a=" |":"\n".join(("",(a*~-j+"  ","-+"*~-j+"- ")[x%2]*(i/j))[j>1]+a*(i,i%j)[j>1]for x in(0,1,2)if i*j)

Try it online!

Old question I know but fun to have a go at anyway. Chance to try out a few tricks I've learned since joining.

ElPedro

Posted 2014-08-20T12:00:46.743

Reputation: 5 301

0

C (207 characters)

The newline right before exit is for legibility only.

#define P printf(
#define t(v)for(a=c;b<=a;a-=b)a-c&&P" "),P v+1),q(b,v);q(a," |");P"\n");
q(n,c){while(n--)P c);}a;b;c;main(){scanf("%d,%d",&c,&b)<2?b=5:0;b&&c||
exit();b==1?b=32767:0;t("| ")t("+-")t("| ")}

scanf usage shamelessy stolen from Allbeert. Notice that this solution does not compile with gcc as it tries to enforce a nonexistant prototype for exit. Compile with a working C compiler like tcc. Also, this function may or may not work on 64 bit platforms. Use with care.

Here is the original ungolfed implementation this is based on:

#include <stdio.h>
#include <stdlib.h>

static void
tally_line(int base, int count, const char *str)
{
     int follower = 0, i;

     /* full tallies first */
     for (; count >= base; count -= base) {
          if (follower++)
               putchar(' ');

          /* only print second character */
          printf(str + 1);

          for (i = 0; i < base; i++)
               printf(str);
     }

     /* partial tally */
     for (i = 0; i < count; i++)
          printf(" |");

     /* newline */
     puts("");
}

extern int
main(int argc, char **argv)
{
     int base, count;

     /* do away with program name */
     count = atoi(*++argv);

     base = argc - 3 ? 5 : atoi(*++argv);

     /* remove 0 later */
     base | count || exit(0);

     /* a crossed-out tally never appears for large numbers */
     if (base == 1)
          base = 32767;

     tally_line(base, count, "| ");
     tally_line(base, count, "+-");
     tally_line(base, count, "| ");

     return (EXIT_SUCCESS);
}

FUZxxl

Posted 2014-08-20T12:00:46.743

Reputation: 9 656