Monday, February 21, 2011

ruby list comprehension

I have seen list comprehension cited as a feature of python that ruby lacks. I want to play around with the idea to see where it goes...

[disclaimer: this is just scratch space for ideas at the moment]

(I wonder how lazy enumerators will change the ruby approach?)

A nice write up on list comprehensions in python has an example where you can use list comprehension to find prime numbers. They divide it into two steps because it gets a little hairy on one line. Here's finding primes on one line using list comprehension:


>>> [x for x in range(2, 50) if x not in [j for i in range(2, 8) for j in range(i*2, 50, i)]]
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]

It's not bad, but it's easy to get lost in there (my ruby eyes are not highly trained in python, of course).

Now, one of the things list comprehensions do is it does selection and modification in the same phrase. This part of list comprehensions is easy to implement.

[ add the equivalent python here ]


(2..10).select(&:even?).map {|v| v**2 }
(2..10).map {|v| v**2 if v.even? }.compact

Implementing this aspect of list comprehensions in ruby is less than trivial:


module Enumerable
  def lc(&block)
    block.nil? ? self : self.map(&block).compact
  end
end

The other part of list comprehensions is the fact that the evaluation happens from right to left (in the python code) so that the execution of the code is efficient.

This gets the job done, but the right-most block is executed multiple times to make it work:

(2..50).reject {|j| (2..8).map {|i| (i*2).step(50,i).to_a}.flatten.include?(j) 

# also other ways to think about this (need to finish this post)
Set.new(2..50) - (2..8).reduce(Set.new) {|np,i| np.merge((i*2).step(50,i)) }

Wednesday, February 9, 2011

Using Dropbox and git

Dropbox is really fantastic, but its version control abilities are limited. Git is a fantastic tool for versioning things.  Here's how I use both to keep detailed version history of the projects that matter to me:

Solo Repo (use a Normal git repo)


For most projects, I do not need to merge changes with multiple people, but I want a version history. For these, I set up a normal git repository. This repo has no "origin", so I never push to anything, just stage and checkin.


cd ~/Dropbox/myproject
git init
git add .
git commit -m 'first commit'
# after a while
git status
git commit -a -m 'another commit' 

Easy to do, and all my changes (even when I don't happen to check them in) follow me on all the computers I use dropbox on.

Shared Repo (use a Bare git repo)


In this case, I'll make a bare git repo and then share the folder with others. I'll assume I'm starting with an existing project directory, but it is not a git repo yet. (there are other ways to do this).


# my project is sitting on my local machine somewhere.
# I will make it a normal git repository
cd someproject
git init
git add .
git commit -m 'first commit'
# now make a bare git repository on my dopbox folder
# by convention, bare repos end in '.git'
mkdir -p ~/Dropbox/git/someproject.git  
pushd ~/Dropbox/git/someproject.git 
git init --bare
popd  # back where I was
git remote add origin ~/Dropbox/git/someproject.git
# now we can push and pull to our origin (see below if you get errors)
git pull
git push

If you get trouble when pushing or pulling to your remote, add these lines to your ~/.gitconfig file:


[push]
 default = matching
[branch "master"]
 remote = origin
 merge = refs/heads/master