Skip to content

Instantly share code, notes, and snippets.

@corbanbrook
Created October 28, 2009 17:20
Show Gist options
  • Save corbanbrook/220626 to your computer and use it in GitHub Desktop.
Save corbanbrook/220626 to your computer and use it in GitHub Desktop.
Better list comprehension in ruby
# better list comprehension by Corban Brook
module Kernel
def list █ Comprehension::List.new █ end
end
module Comprehension
Dimension = Struct.new(:for, :in, :if)
class Context; end
class List
def initialize &expression
@expression = expression
@result = []
@dimensions = []
@context = Context.new
end
def for *symbols
@dimensions << Dimension.new(symbols)
symbols.each { |sym| Context.send(:attr_accessor, sym) }
self
end
def in range
@dimensions.last.in = range
self
end
def if &condition
@dimensions.last.if = condition
self
end
def comprehend
generated = ""
@dimensions.each_with_index do |dimension, i|
block_params = dimension.for.collect {|sym| sym.to_s}.join(", ")
generated << "for #{block_params} in @dimensions[#{i}].in\n"
generated << dimension.for.map {|sym| "@context.#{sym} = #{sym}\n" }.join
if dimension == @dimensions.last
if @dimensions[i].if
generated << "if @context.instance_eval(&@dimensions[#{i}].if)\n"
end
generated << "@result << @context.instance_eval(&@expression)\n"
if @dimensions[i].if
generated << "end\n"
end
end
end
@dimensions.each_with_index do |dimension, i|
generated << "end\n"
end
#puts generated
eval generated
@result
end
end
end
word = "spam"
s = list{[word[0, i], word[i, word.length]]}.for(:i).in(0..word.length).comprehend
p s
#=> [["", "spam"], ["s", "pam"], ["sp", "am"], ["spa", "m"], ["spam", ""]]
deletes = list{a + b[1, b.length]}.for(:a, :b).in(s).if{b.any?}.comprehend
p deletes
#=> ["pam", "sam", "spm", "spa"]
transposes = list{a + b[1,1] + b[0,1] + b[2, b.length]}.for(:a, :b).in(s).if{b.length > 1}.comprehend
p transposes
#=> ["psam", "sapm", "spma"]
alphabet = 'a'..'z'
replaces = list{a + c + b[1, b.length]}.for(:a, :b).in(s).for(:c).in(alphabet).if{b.any?}.comprehend
p replaces
#=> ["apam", "bpam", "cpam", "dpam", "epam", "fpam", "gpam", "hpam", "ipam", "jpam", "kpam", "lpam", "mpam", "npam", "opam", "ppam", "qpam", "rpam", "spam", "tpam", "upam", "vpam", "wpam", "xpam", "ypam", "zpam", "saam", "sbam", "scam", "sdam", "seam", "sfam", "sgam", "sham", "siam", "sjam", "skam", "slam", "smam", "snam", "soam", "spam", "sqam", "sram", "ssam", "stam", "suam", "svam", "swam", "sxam", "syam", "szam", "spam", "spbm", "spcm", "spdm", "spem", "spfm", "spgm", "sphm", "spim", "spjm", "spkm", "splm", "spmm", "spnm", "spom", "sppm", "spqm", "sprm", "spsm", "sptm", "spum", "spvm", "spwm", "spxm", "spym", "spzm", "spaa", "spab", "spac", "spad", "spae", "spaf", "spag", "spah", "spai", "spaj", "spak", "spal", "spam", "span", "spao", "spap", "spaq", "spar", "spas", "spat", "spau", "spav", "spaw", "spax", "spay", "spaz"]
inserts = list{a + c + b}.for(:a, :b).in(s).for(:c).in(alphabet).comprehend
p inserts
#=> ["aspam", "bspam", "cspam", "dspam", "espam", "fspam", "gspam", "hspam", "ispam", "jspam", "kspam", "lspam", "mspam", "nspam", "ospam", "pspam", "qspam", "rspam", "sspam", "tspam", "uspam", "vspam", "wspam", "xspam", "yspam", "zspam", "sapam", "sbpam", "scpam", "sdpam", "sepam", "sfpam", "sgpam", "shpam", "sipam", "sjpam", "skpam", "slpam", "smpam", "snpam", "sopam", "sppam", "sqpam", "srpam", "sspam", "stpam", "supam", "svpam", "swpam", "sxpam", "sypam", "szpam", "spaam", "spbam", "spcam", "spdam", "speam", "spfam", "spgam", "spham", "spiam", "spjam", "spkam", "splam", "spmam", "spnam", "spoam", "sppam", "spqam", "spram", "spsam", "sptam", "spuam", "spvam", "spwam", "spxam", "spyam", "spzam", "spaam", "spabm", "spacm", "spadm", "spaem", "spafm", "spagm", "spahm", "spaim", "spajm", "spakm", "spalm", "spamm", "spanm", "spaom", "spapm", "spaqm", "sparm", "spasm", "spatm", "spaum", "spavm", "spawm", "spaxm", "spaym", "spazm", "spama", "spamb", "spamc", "spamd", "spame", "spamf", "spamg", "spamh", "spami", "spamj", "spamk", "spaml", "spamm", "spamn", "spamo", "spamp", "spamq", "spamr", "spams", "spamt", "spamu", "spamv", "spamw", "spamx", "spamy", "spamz"]
# Python equivalent of above comprehensions (much prettier.. but you do what you gotta do)
s = [(word[:i], word[i:]) for i in range(len(word) + 1)]
deletes = [a + b[1:] for a, b in s if b]
transposes = [a + b[1] + b[0] + b[2:] for a, b in s if len(b)>1]
replaces = [a + c + b[1:] for a, b in s for c in alphabet if b]
inserts = [a + c + b for a, b in s for c in alphabet]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment