Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: Sorting file names randomly



On Jul 23,  9:42pm, DervishD wrote:
}
}     shuffle dir1/* dir2/* ...

There's no reason to noglob and alias this.  The space required to expand
the glob on the command line is no worse than what you're doing inside
the function anyway, and there aren't argument-size limits on calls to
shell functions, only on external commands.

}     I've tried to use '$~' in the solution above (the '%0...' one),
} but it doesn't work because although files in dir1 and files in dir2
} are sorted randomly, dir1 files appear always before dir2 files. It
} seems that the random number doesn't affect the sorting of pathnames

Right, (e:..:) is applied to the base file name, within each directory,
not to the entire string being globbed.

}     Any simple way of using the above solution for this new problem

Not really; glob qualifiers aren't going to do it for you.

} Any simple way of doing the random sort on a group of patterns?

You'll have to first expand them and then sort the resulting array.

On Jul 23, 11:26pm, DervishD wrote:
}
}     The function returns the list in the 'reply' array parameter, and
} prints it on stdout.
} 
}     If anybody can make it better/shorter, suggestions are welcome ;)

The following won't work in versions of zsh that lack the += assignment:

    function shuffle {
      emulate -L zsh
      integer i
      reply=()
      # set -- $~*   # uncomment to use with noglob alias
      for ((i=1; i <= $#; ++i)) { reply[i*RANDOM/32768+1]+=($argv[i]) }
      shift reply
      print -l $reply
    }

The use of array[index]+=(list) means we can insert stuff into the middle
of the array without replacing the stuff that's there.  This has the side
effect that array[1] is always empty (because we always append things
after it), which is why the shift is needed.

So this is a true shuffle; for each "card" $argv[i], we insert it into
the reply "deck" at a random position among the previous i-1 cards.

A more efficient way might be this:

    function shuffle {
      emulate -L zsh
      declare -A h
      local +h -Z 5 RANDOM=$SECONDS
      integer i
      # set -- $~*   # uncomment to use with noglob alias
      for ((i=1; i <= $#; ++i)) { h[$i.$RANDOM]=$argv[i] }
      reply=( $h )
      print -l $reply
    }

This creates random but unique hash keys and then retrieves the shuffled
values in one assignment; we don't care that the order of hash values is
indeterminate, because we want it to be random!  The local RANDOM is
there to force it to be zero-padded to 5 places, so all the hash keys
are the same length; probably not essential.

(Incidentally, I didn't test this, but I'll bet that "seeding" a local
RANDOM like that ruins the repeatable sequence of the global RANDOM.)

-- 
Bart Schaefer                                 Brass Lantern Enterprises
http://www.well.com/user/barts              http://www.brasslantern.com

Zsh: http://www.zsh.org | PHPerl Project: http://phperl.sourceforge.net   



Messages sorted by: Reverse Date, Date, Thread, Author