Ceylon, 1431, 764, 697, 571, 547, 538, 501, 493, 467, 451
shared void p(){print(sum{0,if(exists a=process.arguments[0])if(a!="test")for(c in a)for(b in 0..7)if(c.hash.get(b))1});}
This was the original, ungolfed:
Integer footprintCharacter(Integer b) {
return sum({0, for(i in 0..7) if(b.get(i)) 1 });
}
Integer footPrintString(String s) {
if(s == "test") {return 0;}
return sum({0, for(c in s) footprintCharacter(c.integer)});
}
shared void footprint() {
if(exists s = process.arguments[0]) {
print(footPrintString(s));
} else {
print("This program needs at least one parameter!");
}
}
This takes the argument from a command line parameter ... process.arguments is an (possibly empty) sequence of strings, so before using one of them, we need to check if it actually exists. In the other case we output an error message (this is not required by the question and will be thrown away in the next versions).
Ceylon's sum
function takes a non-empty Iterable of elements of some type which needs to satisfy Summable
, i.e. has a plus
method, like Integer. (It doesn't work with empty sequences because each Summable type will have its own zero, and the runtime has no chance to know which one is meant.)
The elements of a string, or the one bits of an integer, are not a non-empty iterable. Therefore we are using here the feature to build an iterable by specifying some elements, then a "comprehension" (which will get evaluated to zero or more elements). So in the character case we are adding ones (but only when the corresponding bit is set), in the string case we are adding the result of the characters. (The comprehension will only be evaluated when the receiving function actually iterates over it, not when building the Iterable.)
Let's see how we can shrink this down. First, each of the functions is only called at one place, so we can inline them. Also, as mentioned above, get rid of the error message. (764 footprint points.)
shared void footprint() {
if (exists s = process.arguments[0]) {
if (s == "test") {
print(0);
} else {
print(sum({ 0, for (c in s) sum({ 0, for (i in 0..7) if (c.integer.get(i)) 1 }) }));
}
}
}
We don't actually need the inner nested sum
, we can make this one big comprehension. (This saves us 37 footprint points for sum({0,})
, and some more for whitespace, which will be eliminated at the end anyways.) This is 697:
shared void footprint() {
if (exists s = process.arguments[0]) {
if (s == "test") {
print(0);
} else {
print(sum({ 0, for (c in s) for (i in 0..7) if (c.integer.get(i)) 1 }));
}
}
}
We can apply a similar principle to the special cased "test"
string: as in that case the result is 0 (i.e. nothing is contributed to the sum), we can just do this as a part of the summation (but we have to invert the condition). This mainly saves us the print(0);
, some braces and a bunch of indentation spaces, coming down to a footprint of 571:
shared void footprint() {
if (exists s = process.arguments[0]) {
print(sum({ 0, if (s != "test") for (c in s) for (i in 0..7) if (c.integer.get(i)) 1 }));
}
}
We do the same for the first if
, with the side effect that now giving no argument also outputs 0
instead of doing nothing. (At least I thought that would happen here, instead it seems to hang with an eternal loop? Strange.)
shared void footprint() {
print(sum({ 0, if (exists s = process.arguments[0]) if (s != "test") for (c in s) for (i in 0..7) if (c.integer.get(i)) 1 }));
}
We can actually omit the ()
for the sum
function here, using an alternative function call syntax, which uses {...}
instead of ()
, and will fill in comprehensions into iterable arguments. This has footprint 538:
shared void footprint() {
print(sum{ 0, if (exists s = process.arguments[0]) if (s != "test") for (c in s) for (i in 0..7) if (c.integer.get(i)) 1 });
}
Replacing the function name footprint
(40) by p
(3) saves another 37 points, bringing us to 501. (Ceylon function names have to start with lower case characters, so we can't get less than 3 points here.)
shared void p() {
print(sum{ 0, if (exists s = process.arguments[0]) if (s != "test") for (c in s) for (i in 0..7) if (c.integer.get(i)) 1 });
}
The variable names s
(5) and c
(4), i
(4) are also not optimal. Let's replace them by a
(argument), d
(digit?) and b
(bit-index). Footprint 493:
shared void p() {
print(sum{ 0, if (exists a = process.arguments[0]) if (a != "test") for (c in a) for (b in 0..7) if (c.integer.get(b)) 1 });
}
I don't see any remaining non-whitespace optimization, so let's remove the non-needed whitespace (1 point for each space, two for each of the two line breaks):
shared void p(){print(sum{0,if(exists a=process.arguments[0])if(a!="test")for(c in a)for(b in 0..7)if(c.integer.get(b))1});}
When browsing the API, I found that Character.hash actually returns the same value as its integer
attribute. But it has just 14 points instead of 30, so we come down to 451!
shared void p(){print(sum{0,if(exists a=process.arguments[0])if(a!="test")for(c in a)for(b in 0..7)if(c.hash.get(b))1});}
\x7F}~_?{ow7yvu/s\x1F;=znm>k|OW[]^gc\x1Ex\x1D\eef\\'ZY+-VU.St\x173iNM5K6r\x0FG9:q<ljQ\x15\x13pC\aEF8IJL4\x0E21\x16RTh,X*)\x19\v&%\x1A#d\x1C\rab`!\"$(\x180\x05A\x14B\x12\x11DHP\x03\f\x06\n\t\x80\x10\x01@\x04\b\x02 \x00
\$\endgroup\$