7
3
What general tips do you have for golfing in Factor? I'm looking for ideas that can be applied to code golf problems in general that are at least somewhat specific to Factor (e.g. "remove comments" is not an answer).
7
3
What general tips do you have for golfing in Factor? I'm looking for ideas that can be applied to code golf problems in general that are at least somewhat specific to Factor (e.g. "remove comments" is not an answer).
4
All words need correct stack effect declarations in order to compile.
: hello ( -- ) "Hello, World!" print ;
These are long, even if you use single-char identifiers.
: m ( a b c d -- e f ) dup [ asd? ] bi swap = ;
Unless you use something so often that the benefits outwiegh the costs, inlining is often shorter, and : p ( a -- ) print ;
aliasing doesn't save bytes.
4
Factor is impossible to golf. If you're set on winning, don't use it.
I don't just mean it's verbose, like Java or C# or Scala. I mean, because of its functional style, there's a small number of ways to write a given program, and words are long
and whitespace is not your friend
, so it's a bad target, worse than LISP.
Factor's power is in its object model, which is highly verbose. I golf in it because I think it's a cool language, and I enjoy learning it.
The ::
and M::
words replace the normal compile word :
, so that a function has access to lexical variables.
Why use these over :
? Because often, referring to variables by name will be shorter than stack-shuffling with dup swap rot drop over nip etc
.
4
When converting between numbers and strings, the
string>number
number>string
words can be shortened to:
10 base>
10 >base
respectively. Saves 5 chars each.
There are also:
bin>
and >bin
for binaryoct>
and >oct
for octalhex>
and >hex
for hexadecimalthat are all shorter than n base>
, if they happen to suit your needs!
And if you need integers, use 1 /i
instead of >integer
:
5 3 /i
5 3 / >integer = .
=> t
5.77 1 /i
5.77 >integer = .
=> t
EDIT:
If it's about printing, keep an eye on these too:
.b ! prints a number in binary
.o ! prints a number in octal
.h ! prints a number in hexadecimal
(psst!) – cat – 2016-04-24T02:38:30.217
4
this thing is a golfing jewel. http://docs.factorcode.org/content/vocab-math.unicode.html
Here, I should let it speak for itself.
Word Stack effect Equivalent (Byte Char) Savings (best case)
¬ ( obj -- ? ) not 1 2
² ( m -- n ) 2 ^ , 2^ 1 2
³ ( m -- n ) 3 ^ 1 2
¹ ( m -- n ) 1 ^ 1 2
¼ ( -- value ) 1/4 , 1 4 / 1 2
½ ( -- value ) 1/2 1 2
¾ ( -- value ) 3/4 1 2
× ( x y -- z ) * -1 0
÷ ( x y -- z ) / -1 0
Π ( seq -- n ) product 5 6
Σ ( seq -- n ) sum 1 2
π ( -- pi ) pi 0 0
φ ( -- n ) phi 1 2
‰ ( m -- n ) 1000 / 4 5
‱ ( m -- n ) 10000 / 5 6
ⁿ ( x y -- z ) ^ -1 0
⅓ ( -- value ) 1/3 1 2
⅔ ( -- value ) 2/3 1 2
⅕ ( -- value ) 1/5 1 2
⅖ ( -- value ) 2/5 1 2
⅗ ( -- value ) 3/5 1 2
⅘ ( -- value ) 4/5 1 2
⅙ ( -- value ) 1/6 1 2
⅚ ( -- value ) 5/6 1 2
⅛ ( -- value ) 1/8 1 2
⅜ ( -- value ) 3/8 1 2
⅝ ( -- value ) 5/8 1 2
⅞ ( -- value ) 7/8 1 2
Those are the simple constants. Now for the functions:
Word Stack effect Equivalent (Byte Char) Savings (best case)
∀ ( seq quot -- ? ) all? 1 3
∃ ( seq quot -- ? ) any? 1 3
∄ ( seq quot -- ? ) none? 2 4
∈ ( elt seq -- ? ) member? 4 6
∉ ( elt seq -- y ) ∈ ¬ 3 2
∋ ( seq elt -- ? ) swap member? 9 11
∌ ( seq elt -- y ) ∋ ¬ 3 2
− ( x y -- z ) - -1 0
∕ ( x y -- z ) / -1 0
∖ ( s1 s2 -- set ) diff 1 3
√ ( x -- y ) sqrt 1 3
∛ ( x -- y ) ⅓ ^ 2 2
∜ ( x -- y ) ¼ ^ 2 2
∞ ( -- value ) 1/0. 1 3
∧ ( o1 o2 -- ? ) and 0 2
∨ ( o1 o2 -- ? ) or -1 1
∩ ( s1 s2 -- set ) intersect 6 8
∪ ( s1 s2 -- set ) union 2 4
≠ ( o1 o2 -- ? ) = ¬ 1 2
≤ ( x y -- ? ) <= -1 1
≥ ( x y -- ? ) >= -1 1
⊂ ( s1 s2 -- ? ) subset? 4 6
⊃ ( s1 s2 -- ? ) superset? 6 8
⊄ ( s1 s2 -- ? ) ⊂ ¬ 3 2
⊅ ( s1 s2 -- ? ) ⊃ ¬ 3 2
⊼ ( o1 o2 -- ? ) ∧ ¬ 3 2
⊽ ( o1 o2 -- ? ) or ¬ 2 3
⌈ ( x -- y ) floor 2 4
⌊ ( x -- y ) ceiling 4 6
Yes, I did make that table by hand, and yes, I did the math in my head, so there might be some, er, wrong numbers. I'll go write a program to do it for me, now :P
3
split
doesn't need a string
sequence as an argumentOr, more accurately / generally:
Meaning string operations work on sequences of chars too
... which is important because, for instance, " " split
will split a char-array on the number 32.
More directly, this:
>string " " split
is exactly equivalent to
" " split
While being 8 chars longer.
Sometimes "" map-as
for map >string
and family could be handy, I guess. – fede s. – 2016-04-09T19:21:11.170
@fedes. Ooh, that's a good one – cat – 2016-04-11T02:49:28.080
3
Most challenges can be solved with one word definition because of Factor's functional, applicative nature.
I just had the sudden realisation that [ quoting ]
code is the equivalent of a lambda definition in other languages.
I don't know why I didn't think of this before...
A normal word definition:
: f ( a c -- b ) asd asd ;
Lamba'd:
[ asd asd ]
You can do everything inside a [ quotation ]
that you can inside a :
word.
3
The infix
vocabulary is pretty cool.
It allows for infix notation math, which sounds lame since Factor's RPN, but:
IN: scratchpad USE: infix
IN: scratchpad [infix 5-40/10*2 infix] .
-3
For longer expressions it's much shorter than RPN because of whitespace, but you'll need to overcome the [infix
syntax's length.
It also allows Python-style slicing:
USING: arrays locals infix ; [let "foobar" :> s [infix s[0:3] infix] ] . "foo"
Additionally, you can step through sequences with seq[from:to:step] notation.
USING: arrays locals infix ; [let "reverse" :> s [infix s[::-1] infix] ] . "esrever" USING: arrays locals infix ; [let "0123456789" :> s [infix s[::2] infix] ] . "02468"
2
Idiomatic code uses quotations and dip
, but short code uses swap
. Look, you can have an extra swap per two dip
s.
[ dip ] [ dip ] [ dip ] [ dip ] [ dip ]
swap swap swap swap swap swap swap swap
However, sometimes trading swap
for dip
makes the code longer in other places, so it's a bit of a reFactoring job.
"reFactoring job" hahahahahahahahaha why is this funny – Fund Monica's Lawsuit – 2016-04-14T00:53:52.687
@QPaysTaxes I spend too much time laughing at that sort of thing, don't worry :P – cat – 2016-04-14T00:57:44.857
2
Factor is full of higher-order functions that take [ quotations ]
or other-words
and work on sequences / other data.
For example, to take an IP address as a string and sum its fields, you could do:
"." split [ 10 >base ] [ + ] map-reduce 10 base>
"." split [ 10 >base ] map 0 + reduce 10 base>
"." split [ 10 >base ] map sum 10 base>
Notice that fancy map-reduce
is actually longest here because of [ quotation ]
syntax, but there's already a sum
word for that anyways, which is far shorter.
Here's a place map-reduce
is shorter:
[ "." split [ 10 base> ] [ [ 256 * ] dip + ] map-reduce ] bi@ - abs 1 + ]
[ "." split [ 10 base> ] map 0 [ [ 256 * ] dip + ] reduce ] bi@ - abs 1 + ]
Can't use sum
there.
2
I never realised this before, but "strings"
are rather special to the parser.
"asd""abc"
is equivalent to:
"asd" "abc"
and
URL" a"split
is the same as
URL" a" split
This is perhaps the only time whitespace isn't necessary in Factor, and also works for URL" "
, DLL" "
, P" "
, SBUF" "
and the others handled by parse-string
, as pointed out by @fedes. in the comments.
Note well that the opening quote-like marker must be preceded by whitespace.
The following are considered one word:
filter"asdasd"map
filter"asdasd"
And you will get a No word <blah> found in current vocabulary search path
error.
This should work for the closing "
of URL"
, DLL"
, P"
and SBUF"
too, as they all use parse-string
. Good find! – fede s. – 2016-05-02T20:33:19.810
@fedes. Updated, I forgot about those! – cat – 2016-05-02T20:50:19.707
1
The following are equivalent:
1 '[ _ blah ]
1 [ blah ] curry
It's just that the top one is shorter by three bytes.
1
The following are equivalent
[ 1 2 3 ] [ 1 + ] map V{ } clone-like
[ 1 2 3 ] [ 1 + ] V{ } map-as
It's just map-as
and the other -as
words (zip-as
, map-as
, accumulate-as
, etc) are usually shorter than an obj clone-like
if you're going to use them anyways.
That's true for all languages – proud haskeller – 2016-03-25T19:57:04.860
3@proudhaskeller in most modern languages (e.g. Python, JS, what have you), if you use
print
or whatever more than a certain number of times, you'll want to dop=print;p(...)
. In Factor, the cost for the equivalent ofp=print
is prohibitively high. – cat – 2016-03-25T20:01:07.067Not being able to use a common golfing tip is not a golfing tip in itself. – proud haskeller – 2016-03-25T20:01:55.483
Also, a general golfing tip is to inline the methods you wrote, mostly when you used the method only once (but not always), hence a general tip. – proud haskeller – 2016-03-25T20:03:05.150
@proudhaskeller if it really bothers you, feel free to DV / flag this as NAA. – cat – 2016-03-25T20:03:17.410