palindrome = (+(+[] | +[-:_] | ((-'[0]' >> -:ends) & (-'[1..-2]' >> -:p)
& (-'[-1]' >> -:ends) & -:_)) >> (-:'{}' >> -:p)).call
(palindrome =~ []).should_not be_nil
(palindrome =~ [:fish]).should_not be_nil
(palindrome =~ [:fish, :dog]).should be_nil
(palindrome =~ [:fish, :dog, :fish]).should_not be_nil
(palindrome =~ [:fish, :fish, :fish, :fish]).should_not be_nil
(palindrome =~ [:fish, :fish, :fish, :fish, :fish]).should_not be_nil
(palindrome =~ [:fish, :dog, :dog, :fish]).should_not be_nil
(palindrome =~ [:fish, :dog, :cat, :dog, :fish]).should_not be_nil
(palindrome =~ [:fish, :dog, :cat, :fish]).should be_nil
palindrome = (
+( # we'll start by creating a pattern
+[] | # match the empty list, or:
+[-:_] | # match a list containing anything
# (-:_ matches any one entity), or:
( # alternative three:
(-'[0]' >> -:ends) & # extract the first element and
# bind it to 'ends'
(-'[1..-2]' >> -:p) & # and everything from the second to the
# second-last will be matched against a
# pattern :p
(-'[-1]' >> -:ends) & # and the last element must match whatever
# was bound to 'ends' above.
# it's equally effective to include
# -'_[0].eql?(_[1])', same thing.
-:_ # if all that matches, match the entire array
# so that the result is the entire array, not
# whatever part of the array we were just
# matching
) # therein ends the pattern
) >> (-:'{}' >> -:p) # pass the pattern to a closure-maker (-:'{}')
# and then bind it to 'p' so that the pattern
# is now recursive: this pattern matches an
# empty list, a list with one element, or
# a list with the first and last elements eql?
# to each other and whatever is in-between them
# matching the palindrome pattern bound to 'p'.
).call
f = (
+(
(-'==0' >> -'0') |
(-'==1' >> -'1') |
(-'n>1' >> -'n * f[n-1]') ) >> (-:'{}' >> -:f)).call
f[0].should == 0
f[1].should == 1
f[2].should == 2
f[3].should == 6
f[4].should == 24
f[5].should == 120
Labels: ruby