diff options
author | Nicholas Georgescu <36280690+NGeorgescu@users.noreply.github.com> | 2024-05-13 15:10:46 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-13 13:10:46 -0600 |
commit | 154d5272a01e68ec1e0871d75d94fc7bf8a871e0 (patch) | |
tree | 827d129efdba49ee69d3587ac5bcff6c3d5cf796 | |
parent | c3f67d32c8dfd16a08662b8301686f7335a83823 (diff) |
[GolfScript/en] (#4626)
-rw-r--r-- | golfscript.html.markdown | 619 |
1 files changed, 619 insertions, 0 deletions
diff --git a/golfscript.html.markdown b/golfscript.html.markdown new file mode 100644 index 00000000..8ea985e8 --- /dev/null +++ b/golfscript.html.markdown @@ -0,0 +1,619 @@ +--- +language: GolfScript +filename: golfscript.gs +contributors: + - ["Nicholas S Georgescu", "http://github.com/ngeorgescu"] +--- + +GolfScript is an esoteric language that was developed in 2007 by Darren +Smith. It is a scripting language with an interpreter written in Ruby. It lets +you write very dense code in very few characters. The main goal of the language +is, as the name suggests, to solve problems in as few keystrokes as possible. +The examples page on the GolfScript website even has an entire Sudoku solver +written in just 77 characters. + +If you get really good at GolfScript you can easily find yourself using it as a +go-to language for solving some (even somewhat hard) coding problems. It's never +going to be faster than Ruby, but it can be very fast to write, since a single +character of GolfScript can replace an entire line of code in some other languages. + +GolfScript is based on the use of the stack. This tutorial therefore will +read as a sequence of stack operations on an actual stack, as opposed to some +standalone code and individual results. The stack starts as an empty list, and +everything either adds to the stack, or it pops some items off, transforms them, +and puts them back onto the stack. + +To get started running GolfScript, you can get the golfscript.rb file from the +[GitHub repo](https://github.com/darrenks/golfscript). Copy it into your `$PATH`, +(dropping the .rb and chmodding as necessary). You can run GolfScript from either +the interactive interpreter (which mirrors the tutorial below). Once you get the hang +of GolfScript, you can start running from "stdin". If you see a script starting with `~`, +it was probably designed to be dropped in a file and run with `golfscript file.gs`. You +can pipe in or enter in your input at runtime. + +```golfscript +> anything undefined technically evaluates to nothing and so is also a comment +# but commenting it out explicitly anyway is probably a good idea because if +# you use a reserved keyword or any punctuation you'll run into trouble. +[] +> ###################################################################### +# datatypes +######################################################################## +> 1 # Here we add 1 to the stack. Any object entry adds things to the stack +[1] +> 'abc' # here we are adding a string. The only difference between single and +# double quotes is that double lets you escape more things other than \' and \n +# it won't matter for the sake of this tutorial. +[1 "abc"] +> {+} # the third type of object you can put on the stack is a block +[1 "abc" {+}] +> ] # this takes everything prior and puts it into an array, the fourth type +# of object. (besides bug exploits like [2-1?] those are the only four types) +[[1 "abc" {+}]] +> ; # let's clear the stack by executing the discard function on this array. +# if you type the characters ]; it always clears the stack. +[] +> 1"abc"{+}]; # newlines are whitespaces. Everything we did up to this point +# can be put into one line and it all works the exact same. +######################################################################## +# operators and math +######################################################################## +[] +> 1 1 # we add two 1s to the stack. We could also duplicate the first with . +[1 1] +> + # math is done by executing an operation on the top of the stack. This +# can be a standalone character. The way to read this is that we put a 1 on +# the stack, another one one the stack, and then executed a + operation which +# takes the top two elements off of the stack, sums them up, and returns them +# to the stack. This is typically referred to as postfix notation. It can be +# a bit jarring, but this is the way to think about things. You're adding to +# the stack with objects and modifying the top of the stack with operators. +[2] +> 8 1- # minus works the same way. N.B. that we still have that 2 on the stack +# from earlier +[2 7] +> 10 2* # multiplication works the same way. The product is added to the stack +[2 7 20] +> 35 4/ # all division is integer division +[2 7 20 8] +> 35 4% # modulo operation +[2 7 20 8 3] +> 2 3? # exponentiation +[2 7 20 8 3 8] +> 8~ # bitwise "not" function on signed integers +[2 7 20 8 3 8 -9] +> -1~ # this yields 0, which is useful to know for the ? operator +[2 7 20 8 3 8 -9 0] +> 5 3| # or: yields 7, since [1 0 1] | [0 1 1] => [1 1 1] +[2 7 20 8 3 8 -9 0 7] +> 5 3^ # xor: yields 6, since the parity differs at [1 1 0] +[2 7 20 8 3 8 -9 0 7 6] +> 5 3& # and: yields 1, since it's the only bit active in both: [0 0 1] +[2 7 20 8 3 8 -9 0 7 6 1] +> ]; ################################################################### +# booleans +######################################################################## +[] +> 5 3 +[5 3] +> < #add two numbers to the stack, and then perform a lessthan operation +# booleans are False if 0, [], {}, '', and true if anything else. +[0] +> 5 3> # greater than operation. +[0 1] +> 5 3= #single equal is the operator. Again, before the equals is executed, +# the stack reads [0 1 5 3], and then the equals operator checks the top 2 +# values and yields: +[0 1 0] +> ! #not, returns 1 if 0 else 0. +[0 1 1] +> ) #increments the last number +[0 1 2] +> ( #decrements the last number +[0 1 1] +> ]; ################################################################### +# stack control +######################################################################## +[] +> 1 # put a number on the stack +[1] +> . # duplicate the number +[1 1] +> ) # increment +[1 2] +> \ # flip the top two items +[2 1] +> 1$ # $ copies the nth-to-last item on the stack at the index preceding. +# Here we get the 1-indexed item. +[2 1 2] +> 0$ # to copy the 0-indexed item we use the appropriate index. +# This is identical to . operation +[2 1 2 2] +> ) # increment +[2 1 2 3] +> @ # pulls the third item up to the top +[2 2 3 1] +> [@] # use this trick to flip the top 3 items and put them into an array +# if you wrap any operation in brackets it flips the results into an array. +# even math operations like, [+] and [-] +[2 [3 1 2]] +> ]; # also, using at most two strokes you can orient the top three items +# in any permutation. Below are shown the results on 3,~ + # => 0 1 2 (i.e. doing nothing) + # \ => 0 2 1 + # @\ => 1 0 2 + # @ => 1 2 0 + # @@ => 2 0 1 + # \@ => 2 1 0 +[] +> ###################################################################### +# using arrays +######################################################################## +[] +> 2, # comma is the range() function +[[0 1]] +> , # and also the length() function +[2] +> ;4, # let's get an array of four items together +[[0 1 2 3]] +> ) # we can pop off the last value +[[0 1 2] 3] +> + # and put it back +[[0 1 2 3]] +> ( # we can pop off the first value +[[1 2 3] 0] +> \+ # and put it back +[[0 1 2 3]] +> 2- # we can subtract a particular value +[[0 1 3]] +> [1 3] # or a list of values +[[0 1 3] [1 3]] +> - +[[0]] +> ! # boolean operations also work on lists, strings, and blocks. If it's +# empty it's a 1, otherwise 0. Here, the list has a zero, but it's not zero- +# length, so the array as a whole is still True... and hence "not" is False +[0] +> ;4,(+ # let's make a range, pop the first value, and tack it on the end +[[1 2 3 0]] +> $ # we can also restore order by sorting the array +[[0 1 2 3]] +> 1 > # we can also use < > and = to get the indeces that match. Note this +# is not a filter! This is an index match. Filtering items greater than one +# is done with {1>}, +[[1 2 3]] +> 2 < # remember it's zero-indexed, so everything in this array is at an index +# less than 2, the indeces are 0 and 1. +[[1 2]] +> 1= # < and > return an array, even if it's one item. Equals always drops +# it out of the array +[2] +> ;6,2% # the modulo operator works on lists as the step. +[[0 2 4]] +> ;4,2,-:a 3,2+:b # booleans also work on lists. lets define two lists +[[2 3] [0 1 2 2]] +> | # "or" - returns set of items that appear in either list i.e. "union set" +[[2 3 0 1]] +> ;a b& # returns set of items that appear in 1 AND 2, e.g. "intersection set" +[[2]] +> ;a b^ # returns the symmetric difference set between two lists, +[[3 0 1]] +> ~ # tilde unpacks the items from a list +[3 0 1] +> ]; a +[2 3] +> 2? # finds the index of an item +[0] +> ;3a? +[1] +> 4a? # returns -1 if the item doesn't exist. Note: Order of element and array +# doesn't matter for searching. it can be [item list?] or [list item?]. +[1 -1] +> ]; # clear +[] +> 3,[4]* # join or intersperse: puts items in between the items +[[0 4 1 4 2]] +> ; 3,4* # multiplication of lists +[[0 1 2 0 1 2 0 1 2 0 1 2]] +> ;[1 2 3 2 3 5][2 3]/ # "split at" +[[[1] [] [5]]] +> ;[1 2 3 2 3 5][2 3]% # modulo is "split at... and drop empty" +[[[1] [5]]] +> ];#################################################################### +# strings +######################################################################## +# strings work just like arrays +[] +> "use arch, am vegan, drive a stick" ', '/ # split +[["use arch" "am vegan" "drive a stick"]] +> {'I '\+', BTW.'+}% # map +[["I use arch, BTW." "I am vegan, BTW." "I drive a stick, BTW."]] +> n* # join. Note the variable n is defined as a newline char by default +["I use arch, BTW.\nI am vegan, BTW.\nI drive a stick, BTW."] +> n/ # to replace, use split, and join with the replacement string. +[n "Also, not sure if I mentioned this, but" n]{+}* # fold sum 3-item array +* # and use join to get the result +n+ print # and then pop/print the results prettily +I use arch, BTW. +Also, not sure if I mentioned this, but +I am vegan, BTW. +Also, not sure if I mentioned this, but +I drive a stick, BTW. +[] +> '22222'{+}* # note that if you fold-sum a string not in an array, you'll +# get the sum of the ascii values. '2' is 50, so five times that is: +250 +> ]; ################################################################### +# blocks +######################################################################## +[] +> 3,~ # start with an unpacked array +[0 1 2] +> {+-} # brackets define a block which can comprise multiple functions +[0 1 2 {+-}] +> ~ # blocks are functions waiting for execution. tilde does a single +# execution of the block in this case, we added the top two values, 1 and 2, +# and subtracted from 0 +[-3] +> ;10,~{+}5* # multiplication works on executing blocks multiple times +# in this case we added the last 6 values together by running "add" 5 times +[0 1 2 3 39] +> ];10,4> # we can achieve the same result by just grabbing the last 6 items +[[4 5 6 7 8 9]] +> {+}* # and using the "fold" function for addition. +[39] +> # "fold" sequentially applies the operation pairwise from the left +# and then dumps the results. Watch what happens when we use the duplicate +# operator to fold. it's clear what happens when we duplicate and then negate +# the duplicated item: +> ;4,{.-1*}* +[0 1 -1 2 -2 3 -3] +> ]{3%}, # we can filter a list based on applying the block to each element +# in this case we get the numbers that do NOT give 0 mod 3 +[[1 -1 2 -2]] +> ;10,{3%0}, # note that only the last element matters for retaining in the +# array. Here we take 0..9, calculate x mod 3, and then return a 0. The +# intermediate generated values are dumped out sequentially. +[0 1 2 0 1 2 0 1 2 0 []] +> ]; # clear +[] +> 5,{5*}% # map performs each operation on the array and returns the result +# to an array +[[0 5 10 15 20]] +> {.}% # watch what happens when you map duplicate on each item +[[0 0 5 5 10 10 15 15 20 20]] +> ]; ################################################################### +# Control Flow! +######################################################################## +# This is the most important part of scripting. Most languages have +# two main types of loops, for loops and while loops. Even though golfscript +# has many possible loops, only a few are generally useful and terse. For loops +# are implemented using mapping, filtering, folding, and sorting over lists. +# For instance, we can take the factorial of 6 by: +6, # get 0..5 +{)}% # increment the list, i.e. "i++ for i in list" to get 1..6 +{*}* # fold by multiplication , 9 characters for the operator itself. +[720] +> 6),(;{*}* # but can we get shorter? We can save some space by incrementing +# the 6, dropping the zero, and folding. 8 characters. +> # we can also use fold to do the same thing with unfold +1 6 # accumlator and multiplicand, we'll call A and M +{}{ # while M + . # copy M, so now the stack is A M M + @ # bring A to the top, so now M M A + * # apply M to the accumulator, so M A + \( # flip the order, so it's A M, and M-- +}/; # "end", drop the list of multiplicands +# this is effectively a while-loop factorial +[720 720] +> 1.{6>!}{.@*\)}/; # we can also do the same thing with M++ while M not > 6 +> 1 6{.@*\(.}do; # works the same way as the decrementing fold. +[720 720 720] +> ]; #obviously a for loop is ideal for factorials, since it naturally lends +# itself to running over a finite set of items. +######################################################################## +# Writing code +######################################################################## +# Let's go through the process for writing a script. There are some tricks and +# ways to think about things. Let's take a simple example: a prime sieve. +# There are a few strategies for sieving. First, there's a strategy that +# uses two lists, candidates and primes. We pop a value from candidates, +# remove all the candidates divisible by it, and then add it to the primes. +# Second, there's just a filtering operation on numbers. I think it's +# probably shorter to write a program that just checks if a number has no +# numbers mod zero besides 0, 1, and itself. Slower, but shorter is king. +# Let's try this second strategy first. +[] +> 10 # we're probably going to filter a list using this strategy. It's easiest +# to start working with one element of the list. So let's take some example +# where we know the answer that we want to get. +[10] +> .,2> # let's duplicate it and take a list of values, and drop the first two +[10 [2 3 4 5 6 7 8 9]] +> {1$\%!}, # duplicate the ten, and scoot it behind the element, and then run +# 10 element %, and then ! the answer, so we are left with even multiples +[10 [2 5]] +> \; # we want to get rid of the intermediate so it doesn't show up in our +# solution. +[[2 5]] +> 10.,2,-{1$\%!},\; # Okay, let's put our little function together on one line +[[2 5] [2 5]] +> ;; # now we just filter the list using this strategy. We need to negate the +# result with ! so when we get a number with a factor, ! evaluates to 0, and +# the number is filtered out. +[] +> 10,{.,2,-{1$\%!},\;!}, # let's try filtering on the first 10 numbers +[[0 1 2 3 5 7]] +> 2> # now we can just drop 0 and 1. +[[2 3 5 7]] +> 4.?,{.,2,-{1$\%!},\;!},2> # trick: an easy way to generate large numbers in +# a few bytes is duplicate and exponentiate. 4.? is 256, and 9.? is 387420489 +[[2 3 5 7] [2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 +97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 +197 199 211 223 227 229 233 239 241 251]] +> ];'4.?,{.,2,-{1$\%!},\;!},2>', # how long is our code for p<256 ? +[25] +> ; # this is 25 characters. Can we do better?! +[] +> []99,2> # let's go with the first strategy. We'll start with an empty list +# of primes and a list of candidates +[[] [2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 +29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 +55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 +81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98]] +> (\ # pop left and leave left, we're going to copy this value with the filter +[[] 2 [3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 +29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 +55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 +81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98]] +> {1$%}, # filter out anything that is 0 mod by the popped item one back on the +# stack +[[] 2 [3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51 +53 55 57 59 61 63 65 67 69 71 73 75 77 79 81 83 85 87 89 91 93 95 97]] +> @@+ # great, all the 2-divisible values are off the list! now we need to add +# it to the running list of primes +[[3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51 53 55 +57 59 61 63 65 67 69 71 73 75 77 79 81 83 85 87 89 91 93 95 97] [2]] +> \ # swap back. Now it seems pretty clear when our candidates list is empty +# we're done. So let's try it with a do loop. Remember we need to duplicate +# the final value for the pop check. So we add a dot +[[2] [3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51 53 +55 57 59 61 63 65 67 69 71 73 75 77 79 81 83 85 87 89 91 93 95 97]] +> {(\{1$%},@@+\.}do; +[[2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97]] +> ; # ok that worked. So let's start with our initialization as well. +[]4.?,2>{(\{1$%},@@+\.}do; # and let's check our work +[[2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 +103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 +211 223 227 229 233 239 241 251]] +> ,'[]99,2>{(\{1$%},@@+\.}do;', # how long is this? +[26] +> ]; # wow this solution is only 26 long, and much more effective. I don't see +# a way to get any smaller here. I wonder if with unfold we can do better? The +# strategy here is to use unfold and then at the end grab the first value from +# each table. +[] +> 99,2> # start with the candidates list +[[2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 +30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 +56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 +82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98]] +> (\{1$%}, # pop left and filter +[2 [3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51 53 +55 57 59 61 63 65 67 69 71 73 75 77 79 81 83 85 87 89 91 93 95 97]] +> (\{1$%}, # again +[2 3 [5 7 11 13 17 19 23 25 29 31 35 37 41 43 47 49 53 55 59 61 65 67 71 73 77 +79 83 85 89 91 95 97]] +89 91 95 97]] +> {}{(\{1$%},}/ # ok I think it'll work. let's try to put it into an unfold. +[2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 [[5 7 +11 13 17 19 23 25 29 31 35 37 41 43 47 49 53 55 59 61 65 67 71 73 77 79 83 85 +89 91 95 97] [7 11 13 17 19 23 29 31 37 41 43 47 49 53 59 61 67 71 73 77 79 83 +89 91 97] [11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97] [13 +17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97] [17 19 23 29 31 37 41 +43 47 53 59 61 67 71 73 79 83 89 97] [19 23 29 31 37 41 43 47 53 59 61 67 71 73 +79 83 89 97] [23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97] [29 31 37 41 +43 47 53 59 61 67 71 73 79 83 89 97] [31 37 41 43 47 53 59 61 67 71 73 79 83 89 +97] [37 41 43 47 53 59 61 67 71 73 79 83 89 97] [41 43 47 53 59 61 67 71 73 79 +83 89 97] [43 47 53 59 61 67 71 73 79 83 89 97] [47 53 59 61 67 71 73 79 83 89 +97] [53 59 61 67 71 73 79 83 89 97] [59 61 67 71 73 79 83 89 97] [61 67 71 73 +79 83 89 97] [67 71 73 79 83 89 97] [71 73 79 83 89 97] [73 79 83 89 97] [79 83 +89 97] [83 89 97] [89 97] [97]]] +> ;] # drop that list of candidates generated at each step and put the items +# left behind by the unfold at each step (which is the primes) into a list +[[2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97]] +> ]; # clear and let's try with larger numbers +[] +> 4.?,2>{}{(\{1$%},}/;] +[[2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 +103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 +211 223 227 229 233 239 241 251]] +>;'4.?,2>{}{(\{1$%},}/;]', # find the length of our solution. +[21] +> ]; # only 21 characters for the primes! Let's see if we actually can use this +# strategy of leaving items behind, now using the do loop to get even shorter! +> 3.?,2> # candidates +[[2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26]] +> (\{1$%}, # pop and filter +[2 [3 5 7 9 11 13 15 17 19 21 23 25]] +> (\{1$%}, # again! +[2 3 [5 7 11 13 17 19 23 25]] +> {(\{1$%},.}do;] # try in a do loop and drop the empty list of candidates at +# the end of the do loop. Don't forget the dot before the closing brace! +[[2 3 5 7 11 13 17 19 23]] +> ;4.?,2>{(\{1$%},.}do;] # check our work +[[2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 +103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 +211 223 227 229 233 239 241 251]] +> ;'4.?,2>{(\{1$%},.}do;]', +[21] +>]; # Still 21 characters. there's one other thing to try, which is the prime +# test known as Wilson's theorem. We can try filtering the items down using +# this test. +[] +> '4.?,2>{.,(;{*}*.*\%},'.~\, # let's run it and take the length +[[2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 +103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 +211 223 227 229 233 239 241 251] 21] +> ; # Still 21 characters! I think this number is quite good and it's not +# obvious how to beat it. The problem with GolfScript is there's always someone +# out there who thinks of some trick you didn't. For instance, you might think +# you're doing well with a Collatz seq generator of {(}{.2%{3*)}{2/}if}/ until +# you find that someone figured out {(}{3*).2%6\?/}/ which is so much shorter +# and cleaner - the unfold operation is nearly half the length! +######################################################################## +# How to read GolfScript +######################################################################## +# let's take the gcd from the GolfScript banner. It starts with: +[] +> '2706 410'~ # so that's pretty straightforward, that it just evals the list +# and dumps the results on the stack. It's common to read from stdin which +# necessitates unpacking with ~ +[2706 410] +> . # we want to know what that do loop does. the best way to do that is to +# drop the braces and run the loop one command at a time. We duplicate +[2706 410 410] +> @\ # We rearrange +[410 2706 410] +> % # we take the modulo +[410 246] +> .@\% # repeat. Note we don't need to run the final dot before the closing +# brace since this is just a value that is popped to check the loop condition +# you can also replicate the loop end with a semicolon to pop it yourself. +[246 164] +> .@\% # again! +[164 82] +> .@\% # and finally we hit zero. The loop would exit and ; would pop the zero, +# leaving you with the gcd of 82. +[82 0] +> ;; 2706 410{1$1$%.}do # Clearly this involves knowing about Euclid's method. +# you can also try a more obvious method like this one here which shows the +# numbers in sequence. +[2706 410 246 164 82 0] +>]; # so sometimes it pays dividends to know the math and you can write short +# algorithms that rely on easy tricks that aren't immediately obvious. +[] +> # let's try looking at the sudoku solver that is on the examples page. I'll +# skip the unpack step. +[2 8 4 3 7 5 1 6 9 0 0 9 2 0 0 0 0 7 0 0 1 0 0 4 0 0 2 0 5 0 0 0 0 8 0 0 0 0 8 +0 0 0 9 0 0 0 0 6 0 0 0 0 4 0 9 0 0 1 0 0 5 0 0 8 0 0 0 0 7 6 0 4 4 2 5 6 8 9 7 +3 1]:a 0?:b # again the grid is put into an array. Now, the next step +# is to define the "@" symbol as the working grid. This is because "@9" is +# interpreted as two symbols, whereas if you used something like "a" as the +# variable "a9" is interpreted as a single symbol, and this is not defined, +# so it will not get run at execution time. You would need a space which is an +# additional char. On the other hand, redefining built-ins is confusing so I +# will use "a" and "b" for the "@" and "^" definitions respectively. So the +# grid is "a" and the zero-index location of the first zero is "b", at index 9. +[9] +> ~! # this makes sure that the value is not -1 for find, i.e. -1~ evaluates to +# 0 so a ! makes it nonzero. ?~! is a great trick for "isn't in the list" +[0] +> {@p}* # this prints out the grid the number of times as the previous value, +# which is how this thing "finishes". So if 0 isn't in the grid, it prints. +> 10, # let's get the digits 0-9. Zero will be eliminated because our original +# value is zero so when we look in any row or column, zero is guaranteed to be +# there. +[[0 1 2 3 4 5 6 7 8 9]] +> a 9/ # split the original grid row-wise +b 9/ # get the row of our checked value, in this case the second row += # and we get that row and +- # take those numbers off the candidates +[[1 3 4 5 6 8]] +> a # put the grid on the stack +b 9% # get the column of the zero +> # drop the first x values of the grid +9% # take every ninth digit. We now have the column the zero is in +> - # pull those items off the candidates list +[[1 3 5 6]] +> a 3/ # split the grid into three-long arrays +b 9% # get the column of the zero +3/ # is the column in the left (0), middle (1), or right (2) triad? + > # pull that many three-groups off +3% # get every third. Now we have 9 groups - the left side of the grid +3/ # divide those 9 groups it into thirds +b 27/ # was the zero on top (0), middle (1), or bottom (2) third of the grid? += # since it's the top, grab the top group of triads. You now have the + # 1/9th of The sudoku grid where the zero sits +[[1 3 5 6] [[2 8 4] [0 0 9] [0 0 1]]] +> {+}*- # flatten those lists and remove those items from the candidates +# We now have the possible values for the position in question that work given +# the current state of the grid! if this list is empty then we've hit a +# contradiction given our previous values. +[[3 5 6]] +> 0= # {a b<\+a 1 b+>+}/ # now we've hit this unfold operation. If you run it +# you'll find we get the grids back. How does that work?! Let's take the first +# value in the "each" []{}/ operation. This is the best way to figure out what +# is happening in a mapping situation. +[3] +> a # get the grid +b< # get the grid up to the zero +\+ # and tack on our value of 3. +[[2 8 4 3 7 5 1 6 9 3]] +> a 1b+>+ # and we add on the rest of the grid. Note: we could do 1 char better +# because 1b+ is equivalent to but, longer than, just b) +[[2 8 4 3 7 5 1 6 9 3 0 9 2 0 0 0 0 7 0 0 1 0 0 4 0 0 2 0 5 0 0 0 0 8 0 0 0 0 8 +0 0 0 9 0 0 0 0 6 0 0 0 0 4 0 9 0 0 1 0 0 5 0 0 8 0 0 0 0 7 6 0 4 4 2 5 6 8 9 7 +3 1]] +> 1;; # and the do block runs again no matter what. So it's now clear why this +# thing exists with an error: if you solve the last digit, then this loop just +# keeps on rolling! You could add some bytes for some control flow but if it +# works it works and short is king. +[] + +# Closing Tips for getting to the next level: +# 0. using lookback might be more effective than swapping around the values. +# for instance, 1$1$ and \.@.@.\ do the same thing: duplicate last two items +# but the former is more obvious and shorter. +# 1. golfscript can be fun to use for messing around with integer sequences or +# do other cool math. So, don't be afraid to define your own functions to +# make your life easier, like +> {$0=}:min; {$-1=}:max; {.,(;{*}*.*\%}:isprime; {.|}:set; # etc. +# 2. write pseudocode in another language or port a script over to figure out +# what's going on. Especially useful when you combine this strategy with +# algebra engines. For instance, you can port the examples-page 1000 digits +# of pi to python and get: +# import sympy as sp +# a, k = sp.var('a'), list(range(20))[1::2] +# for _ in range(len(k)-1): +# m = k.pop() +# l = k.pop() +# k.append(((l+1)//2*m)//(l+2)+2*a) +# print(str(k[0])) +# which gives "2*a + floor(2*a/3 + floor(4*a/5 + 2*floor(6*a/7 + 3*floor( +# 8*a/9 + 4*floor(10*a/11 + 5*floor(12*a/13 + 6*floor(14*a/15 + 7*floor(16* +# a/17 + 72/17)/15)/13)/11)/9)/7)/5)/3)"... which makes it much more obvious +# what's going on than 10.3??:a;20,-2%{2+.2/@*\/a 2*+}* especially when +# you're new to the language +# 3. a little math goes a long way. The above prime test uses Wilson's theorem +# a comparable program testing for factors {:i,(;{i\%!},(;!}:isprime is +# longer and slower. Also, as discussed above, Collatz is much shorter if +# you recognize that you can do (3x+1) and then divide by 6 to the power +# ((3x+1) mod 2). (If x was even, (3x+1) is now odd, so 3x+1 div 6 is x/2.) +# avoiding conditionals and redundancy can sometimes require such insight. +# And of course, unless you know this continued fraction of pi it's hard to +# calculate it in a terse block of code. +# 4. don't be afraid to define variables and use arrays! particularly if you +# have 4 or more items to shuffle. +# 5. don't be afraid to use [some_long_script] to pack a bunch of items in an +# array after the fact, rather than gathering or adding them later or +# forcing yourself to use a datastructure that keeps the items in an array +# 6. sometimes you might get in a jam with - followed by an int that can be +# solved with ^ to do a symmetric set difference without adding a space +# 7. "#{require 'net/http';Net::HTTP.get_response(URI.parse(address)).body}" +# can get any page source from the internet, substituting 'address' for your +# URL. Try it with an OEIS b-file or wordlists, etc. You can also use the +# shorter "#{File.open('filename.txt').read}" to read in a file. GolfScript +# can run "#{any_ruby_code_here}" and add the results to the stack. +# 8. you can set anything to mean anything, which can be useful for golf: +# 3:^;^2? => 9 because this set ^ to 3, and 3 2 ? => 9 +# 3:a;a2? => Warning: pop on empty stack - because a2 doesn't exist +# 3:a;a 2? => 9 - it works again, but takes an extra character over ^2 +# usually you will only want to do this once you're trying to squeeze the +# last few chars out of your code because it ruins your environment. +``` + +* [Run GolfScript online](https://tio.run/#golfscript) +* [GolfScript's documentation](http://www.golfscript.com/golfscript/builtin.html) +* [Useful StackExchange thread](https://codegolf.stackexchange.com/questions/5264/tips-for-golfing-in-golfscript) +* [GolfScript on GitHub](https://github.com/darrenks/golfscript) |