# I documented this just for you, /prog/. What now, >>1?
class Cycle
def cycle(*args) # All args are passed as an array object
# Assign a new hashtable to the @cycles instance variable if it
# doesn't already exist. If you try to access @cycles[x] for any
# key x that is not already in @cycles, a default value of -1 will
# automatically be inserted. By the way, there's absolutely no
# reason to use an underscore at the beginning of an ivar name.
# The @ already marks it as an ivar.
@cycles ||= Hash.new(-1)
h = args.hash # Hashcode, not array-as-hashtable
@cycles[h] += 1 # Tragically, there's no ++ in ruby
# Have you studied your modular arithmetic today? By the way,
# this is an implicit return statement
args[@cycles[h] % args.size]
end
end
# Now to demonstrate what it actually does
foo = Cycle.new
foo.cycle :hax, :my, :anus # => :hax
foo.cycle :hax, :my, :anus # => :my
foo.cycle :achieve, :satori # => :achieve
foo.cycle :hax, :my, :anus # => :anus
foo.cycle :hax, :my, :anus # => :hax
>>1
If what >>12 said was right, here's how to do it in CL:
(let ((ht (make-hash-table :test #'equal)))
(defun cycle (&rest args)
(unless (gethash args ht)
(setf (gethash args ht) args))
(pop (gethash args ht))))
And a version with less repetition:
(let ((ht (make-hash-table :test #'equal)))
(defun cycle (&rest args)
(symbol-macrolet ((h (gethash args ht)))
(unless h (setf h args))
(pop h))))
Name:
Anonymous2009-08-27 1:23
>>15
Still longer than >>1-chan's Ruby version. I'm masturbating to it as we speak!
Name:
David Caruso2009-08-27 1:24
>>14 If what >>12 said was right
UMH memesmith....
>>16
It does take slightly longer to digest for someone not familiar with Ruby, but the same is probably true for my CL version if people don't know CL.
I'm curious how would the Haskell version be written, especially since OP's problem requires having state implicitly (no passing around a state object).
What >>22 is trying to say is that this is pointless to even try in Haskell. The type system forces you to either write a special monad specifically for the purpose of calling cycle[/cycle], or fuck around with the IO monad so at least you can intersperse it with IO calls.
Oh, and all the arguments have to be of the same type....Actually, it's even worse than that. Since you need a way to compare argument lists to each other, the type signature should say [code](Eq a) => stuff or (Ord a) => stuff, which means that a can't be a function type, which means you can't even use a list like [print 4, print 2]. So basically this (toy) function is more useless than ever in Haskell.
There is a way around this, but it means using separate functions for setting and getting the argument list (and still using a special monad). If you have setArgs :: [a] -> State [a] () and getArg :: State [a] a, the implementation is trivial. The catch is that you can only access the last arglist you set with setArgs, and if you've got different types of arglists you want to use, you have to actually thread them through different instances of State [a].
>>27
As a matter of principle I never use a language that doesn't properly support &optional, &key, and &rest.
Name:
Anonymous2009-08-27 16:13
>>28
I haven't seen a language other than CL with proper support of lambda-list-like features. I've seen some scripting languages try and hack around support for them, but not as good, and I'm not even saying anything about defmacro's arguments being passed as if by destructuring-bind. Yes, there are some insignificant (these days) performance costs to using complex key args, but it makes functions/code much more manageable/generic.
>>25
Yet it would take a lot more code than >>1's or >>14-15's to write the equivalent functionality, and passing variable length args would be awkward unless you passed argument count or just used a list or null-terminated array to represent them. You will have to write a fairly generic hash table implementation(or use one already available), and write a bunch of other boiler-plate code, unless you already have some architecture behind you on which you can lean.
Name:
Anonymous2009-08-27 16:25
from collections import defaultdict
class Cycle(object):
def __init__(self):
self.cycles = defaultdict(int)
c = Cycle()
for i in xrange(6):
print c.cycle("this","is","shit")
Name:
Anonymous2009-08-27 16:53
import Data.Map
import Data.IORef
newCycle =
do state <- newIORef empty
let memoCycle args =
do modifyIORef state $ alter (Just . maybe (cycle args) tail) args
fmap (head . (!args)) $ readIORef state
return memoCycle
>>31
I just thought about writing something using IORefs just to show >>24-26 that it's possible. Good thing you were faster though, as I've never used IORefs before.
>>44
Haskell is the best choice for functional programming.
Perl is the best choice for nothing because ruby is better in every way at the exact same thing.
function cycle(...)
local index = 1
local array = {...}
local length = table.getn(array)
return function()
local value = array[index]
index = index + 1
if index > length then index = 1 end -- indecies starting at one kind of blows for this.
return value
end
end
for v in cycle('the', 'other', 'day', 'I', 'was', 'saying') do
print(v)
end
function cycle(...)
local array = {...}
local length = table.getn(array)
local loop
loop = function(index)
coroutine.yield(array[index])
if index < length then
return loop(index + 1)
else
return loop(1)
end
end
return coroutine.wrap(function()
loop(1)
end)
end
for v in cycle('yaba', 'daba', 'doo') do
print(v)
end
- Ruby indulges obfuscation: Ruby has no keyword/optional arguments, so you'll have to use hash parameters as a substitute. This is an idiom that comes from Perl. Ugly, Perl-looking code, like proc {|obj, *args| obj.send(self, *args)} or (0..127).each { |n| p n.chr }, considered beautiful. Another confusing Perl borrowing are postfix `if` and `while` (line = file.readline while line != "needle" if valid line) and quirky variable names (partially due to naive environment design): @instance_var, @@class_var, CONSTANT_VAR, $global_var, :sym, &proc, $~[1], $!, $>, $@, $&, $+, $0, $~, $’, $`, $:, $., $* and $?. If A is [1,2,3] and B is [10,20,30], then A+B is [1,2,3,10,20,30], when you probably wanted [11,22,33]. A good amount of your code will consist of begin end begin begin end end...
- Faulty syntax. Ruby cant distinguishing a method call from an operator: "a +b" can be both "a(+b)" and "a + b" - remove the space to the left of "+" or add a space to the right of "+", and it will be parsed as an addition. Same with "puts s *10", which is parsed as puts(s(*10)). Ruby's expressions terminate by a newline and you have to implicitly state that the expression is not over, using trailing + or \. That makes it easy to make a dumb syntactic mistake by forgeting to continue line. It also encourages putting everything onto a single line, producing messy looking code.
- Slow: JIT-compiling implementations exist, but they're still slow and incomplete, due to Ruby's complexity and bad design, which make Ruby difficult to optimize compared to other dynamic languages, like Lisp. For example, Ruby has to accomodate for somebody in another thread changing the definition of a class spontaneously, forcing compiler to be very conservative. Compiler hints, like `int X` from C/C++ or `declare (int X)` from Lisp, arent possible either.
- Ruby's GC is a naive mark-and-sweep implementation, which stores the mark bit directly inside objects, a GC cycle will thus result in all objects being written to, making their memory pages `dirty` and Ruby's speed proportional to the number of allocated objects. Ruby simply was not designed to support hundred thousand objects allocation per second. Unfortunately, that’s exactly what frameworks like Ruby on Rails do. The more objects you allocate, the more time you "lose" at code execution. For instance something as simple as 100.times{ ‘foo’ } allocates 100 string objects, because strings are mutable and therefore each version requires its own copy. A simple Ruby on Rails 'hello world' already uses around 332000 objects.
- OOP: Matz had a bit too much of the "OOP is the light and the way" philosophy in him, in effect Ruby doesn't have stand-alone functions and Ruby's blocks can't be used in exactly the same way as usual closures. Even high-order functions are attached to objects and produce verbose code: "names.map { |name| name.upcase }", instead of simple "map upcase names".
- Ruby (like most other scripting languages) does not require variables to be declared, as (let (x 123) ...) in Lisp or int x = 123 in C/C++. If you want a variable private to a block, you need to pick an unique variable name, holding the entire symbol table in your head. Ruby introduces new variables by just parsing their assignements, meaning "a = 1 if false; a" wont raise an error. All that means Ruby can't detect even a trivial typo - it will produce a program, which will continue working for hours until it reaches the typo. Local and global scopes are unintuitive. Certain operations (like regular expression operator) create implicit local variables for even more confusion.
- Non-othogonal: {|bar| bar.foo}, proc {|bar| bar.foo}, lambda {|bar| bar.foo}, def baz(bar) bar.foo end - all copy the same functionality, where Lisp gets along with only `lambda`. Some Ruby's features duplicate each other: print "Hello", puts "Hello", $stdout<<"Hello", printf "Hello", p "Hello", write "Hello" and putc "Hello" -- all output text to stdout; there is also sprintf, which duplicates functionality of printf and string splicing. begin/do/then/end, {} and `:` also play role in bloating syntax, however, in some cases, precedence issues cause do/end and {} to act differently ({} binds more tightly than a do/end). More bloat comes from || and `or`, which serve the same purpose.
- Ruby as a language supports continuations via callcc keyword. Ruby's callcc is incredibly slow, implemented via stack copying. JRuby and IronRuby don't have continuations at all, and it's quite unlikely they will ever get them. There were also support breaches in mainline Ruby, where Ruby 1.9 has not supported continuations for a while. If you want your code to be portable, I'd suggest not using Ruby.
- Ruby was created "because there was no good scripting language that could handle Japanese text". Today it's mostly Rails hype and no outstanding feature, that makes the language, like the brevity of APL or simplicity and macros of Lisp. "There is some truth in the claim that Ruby doesn’t really give us anything that wasn’t there long ago in Lisp and Smalltalk, but they weren’t bad languages." -- Matthew Huntbach
function a(n) return if true then 1 else 2 end end
stdin:1: unexpected symbol near 'if'
oh no, wait:
function a(n)
>> local results = {[true] = 1, [false] = 54}
>> return results[n < 5]
>> end print(a(3))
1 print(a(4))
1 print(a(5))
54 print(a(8))
54
Name:
Anonymous2012-01-28 1:28
ruby is fine art
Proc.send(:define_method, :-@) {self}
class Bow;def >>(v);@arrow = v;end;def shoot(arg);@arrow.call(arg);end;end;bow=Bow.new
class Person;def initialize;@profession=:adventurer;end;def wound;@profession=:guard;end;end;adventurer=Person.new
It seems like this ought to be pretty easy in haskel, to make an infinitely long lazy list that cycles through the content of a finite lazy list. I don't know haskel though, so I can't write it.
Name:
Anonymous2012-01-28 13:55
ponup
Name:
Anonymous2012-01-28 15:09
>>102
Why speculate about something that you aren't that sure about?
>>107
There have to be somethings that you don't know. Whether or not that comes up in conversation wont change that. If you don't talk about them, how can you expect to learn about them?