Scrambler one-liner

I just came across this interesting article at Slashdot that explains that
according to some English university the order of letters in words are not
really relevant for reading as long as the first and the last are at the
right place. (It seems that do not hold in Spanish).

There is a link to a Perl filter[*] that munges a given text that way and I
wanted to write a mock-up as a one-liner, but in Ruby this time. This is
what I have for now:

-pe ‘gsub!(/\w+/){|w|r=1…w.size-1;w[r]=w[r].split(//).sort_by{rand}.to_s;w}’

The cool Array.sort_by { rand } trick is a shuffle I saw in Freenode#ruby-lang
last week, I would like very much to remember the nick of who showed it but I
can’t remember.

It seems one-liners are not at the heart of the Ruby community, but hey,
that’s funny in the end. Can you shorten it?

– fxn

[*] http://www.jwz.org/hacks/scrmable.pl

Alternatively, you could write a version that accepts a “Maximum
distance” for letters to be moved from their original position. Most
people notice after playing for a short while that it doesn’t work
so well for longer words; I’m curious whether it’s actually a limit
of “total shuffle distance”.

···

In article 200309161631.48824.fxn@hashref.com, Xavier Noria wrote:

-pe ‘gsub!(/\w+/){|w|r=1…w.size-1;w[r]=w[r].split(//).sort_by{rand}.to_s;w}’

The cool Array.sort_by { rand } trick is a shuffle I saw in Freenode#ruby-lang
last week, I would like very much to remember the nick of who showed it but I
can’t remember.

It seems one-liners are not at the heart of the Ruby community, but hey,
that’s funny in the end. Can you shorten it?

shrug I use ruby for my one liners all the time…

% ruby -e ‘p File.stat(“/etc/passwd”).mtime’
Fri May 30 15:17:19 EDT 2003

or more horribly

% ruby -rdbi -e ‘rows = ; DBI.connect(“dbi:Pg:somedb:localhost”, “postgres”, “XXXXX”)
{ |dbi| dbi.prepare(“select cisco, cisco_slot, cisco_port, liu_number, liu_port, floor
from new_fiberx where cisco is not null and cisco_slot is not null and cisco_port is
not null and liu_number is not null and liu_port is not null order by cisco, cisco_slot,
cisco_port”) { |sth| sth.execute; sth.each {|row| rows << row.clone } ; rows.sort_by
{ |row| [ row[0], row[1].to_i, row[2].to_i ] }.each { |row| next if row[0] == “” ;
printf “%s.%s/%d : %d.%s/%-5s\n”, row[0], row[1], row[2], row[5], row[3], row[4] }}}’

etc…

Jim

···

On Tue, 16 Sep 2003 23:28:01 +0900 Xavier Noria fxn@hashref.com wrote:

It seems one-liners are not at the heart of the Ruby community, but hey,
that’s funny in the end. Can you shorten it?

This is hilarious, because a friend and I just had (over lunch) a race
to see who could code it up first. I used Ruby and he used Perl. Mine
worked first, but then I reworked it a bit to make it neater. His was
impossible to read, but I totally have to send him this one-liner. Man,
sort_by{rand} is so awesome! I can’t believe I didn’t think of that!

-Kurt

···

On Tue, Sep 16, 2003 at 11:28:01PM +0900, Xavier Noria wrote:

I just came across this interesting article at Slashdot that explains that
according to some English university the order of letters in words are not
really relevant for reading as long as the first and the last are at the
right place. (It seems that do not hold in Spanish).

There is a link to a Perl filter[*] that munges a given text that way and I
wanted to write a mock-up as a one-liner, but in Ruby this time. This is
what I have for now:

-pe ‘gsub!(/\w+/){|w|r=1…w.size-1;w[r]=w[r].split(//).sort_by{rand}.to_s;w}’

The cool Array.sort_by { rand } trick is a shuffle I saw in Freenode#ruby-lang
last week, I would like very much to remember the nick of who showed it but I
can’t remember.

It seems one-liners are not at the heart of the Ruby community, but hey,
that’s funny in the end. Can you shorten it?

– fxn

[*] http://www.jwz.org/hacks/scrmable.pl

======= End of Original Message =======<

Hi –

···

On Tue, 16 Sep 2003, Xavier Noria wrote:

I just came across this interesting article at Slashdot that explains that
according to some English university the order of letters in words are not
really relevant for reading as long as the first and the last are at the
right place. (It seems that do not hold in Spanish).

There is a link to a Perl filter[*] that munges a given text that way and I
wanted to write a mock-up as a one-liner, but in Ruby this time. This is
what I have for now:

-pe ‘gsub!(/\w+/){|w|r=1…w.size-1;w[r]=w[r].split(//).sort_by{rand}.to_s;w}’

The cool Array.sort_by { rand } trick is a shuffle I saw in Freenode#ruby-lang
last week, I would like very much to remember the nick of who showed it but I
can’t remember.

It seems one-liners are not at the heart of the Ruby community, but hey,
that’s funny in the end. Can you shorten it?

A little:

gsub!(/\w+/){|w|w[1…-2]&&=w[1…-2].split(//).sort_by{rand}.join;w}

(Yay! A use for &&=! :slight_smile:

David


David Alan Black
home: dblack@superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

Cool. I was going to post something about that, but I hadn’t bothered to
write the code.

But for the really interesting part, who can make either the shortest or
most efficient descrambler? Here’s a Perl version from Slashdot:

#!/usr/bin/perl
open A,“/usr/share/dict/words”||die;sub
a{$b=b(split//,$[0]);map{uc;($b->{$}!=$[1]->{$ }?return
0:1)}keys%{$[1]}}sub b{undef$z;map{$z->{uc$}++}@;$z}$d=$ARGV[0];$c=b(
split//,$ARGV[0]);map{print"$
\n"}grep{chomp;/[1]+$/i&&a$_,$c};close
A;

I haven’t even tried to understand this, but it is very slow. Seems to
work, though. I’ll post if I get anything.

···

On Tue, 16 Sep 2003 23:28:01 +0900, Xavier Noria wrote:

I just came across this interesting article at Slashdot that explains
that according to some English university the order of letters in words
are not really relevant for reading as long as the first and the last
are at the right place. (It seems that do not hold in Spanish).


Tom Felker,
tcfelker@mtco.com
http://vlevel.sourceforge.net - Stop fiddling with the volume knob.

IBM invented FUD, and they’re not about to be outFUDed by SCO.


  1. $d ↩︎

Hi three,

it semes that this viesorn is a bit sllmear than the oehtr one,
Itnliseegtnry it’s a non ruisbt frenid of mine who sgtgeesud the use of
\B :

gsub!(/\B\w+\B/){$&.split(//).sort_by{rand}.join}

Pierre.

···


Pierre Baillet
Even if one’s head were to be suddenly cut off, he should be able to do one
more action with certainty. With martial valor, if one becomes like a
revengeful ghost and shows great determination, though his head is cut off, he
should not die.
Ghost Dog - The Way of the Samouraï

Cool! :slight_smile: I have just written this version even shorter:

-pe ‘gsub!(/(\w)(\w+)(?=\w)/){$1+$2.split(//).sort_by{rand}.to_s}’

– fxn

···

On Tuesday 16 September 2003 20:07, Kurt M. Dresner wrote:

This is hilarious, because a friend and I just had (over lunch) a
race to see who could code it up first. I used Ruby and he used
Perl. Mine worked first, but then I reworked it a bit to make it
neater. His was impossible to read, but I totally have to send him
this one-liner. Man, sort_by{rand} is so awesome! I can’t believe I
didn’t think of that!

Hi,

i’m so stupid. This friend reads fxn diary on advogato.

Sorry for the noise.

···

On Wed, Sep 17, 2003, Pierre Baillet wrote:

[noise]

Pierre Baillet
Among the maxims on Lord Naoshige’s wall, there was this one: “Matters of great
concern should be treated lightly.” Master Ittei commented, “Matters of small
concern should be treated seriously.”
Ghost Dog - The Way of the Samouraï

OK, here’s the unscrambler code. Be warned, I chose a rather memory
intensive algorithm, it takes about 110 MB of memory with my 2.4 MB words
file. I don’t know how the Perl I gave works, but it only took about 35
MB. Anyway, it’s very fast after the 5 seconds or so to load the
word list.

It works by reading a word list to make a hash, with the keys being the
word alphabetically instead of randomly scrambled, and the values being
the list of unscrambled words. I was amazed at how many words are unique.

#!/usr/bin/env ruby

class Unscrambler
def initialize(wordsFilename = “/usr/share/dict/words”)
@wordsHash = Hash.new{ Array.new }
File.open(wordsFilename) do |wordsFile|
wordsFile.each_line do |word|
word.chomp!
@wordsHash[Unscrambler::word_to_key(word)] <<= word
end
end
end

def Unscrambler::word_to_key(word)
	return word unless word.size > 3
	array = word.split(//)
	array[1..-2] = array[1..-2].sort
	return array.join;
end

def unscramble(word)
	return @wordsHash[Unscrambler::word_to_key(word)]
end

end

puts “Loading wordlist…” if $stdin.isatty

u = Unscrambler.new

puts “Ready.” if $stdin.isatty

$stdin.each_line do |line|
line.gsub!(/\w+/) do |match|
choices = u.unscramble(match.downcase)
case choices.size
when 0 then match
when 1 then choices[0]
else “[” + choices.join(", ") + "]"
end
end
print line
end

···


Tom Felker, tcfelker@mtco.com
http://vlevel.sourceforge.net - Stop fiddling with the volume knob.

Torvalds, explaining SCO’s actions: “They are smoking crack.”

Try

-pe 'gsub!(/\B\w+\B/){$&.split(//).sort_by{rand}.join}

Shorter (and even cleaner too!)

-Kurt

···

On Wed, Sep 17, 2003 at 03:20:53AM +0900, Xavier Noria wrote:

On Tuesday 16 September 2003 20:07, Kurt M. Dresner wrote:

This is hilarious, because a friend and I just had (over lunch) a
race to see who could code it up first. I used Ruby and he used
Perl. Mine worked first, but then I reworked it a bit to make it
neater. His was impossible to read, but I totally have to send him
this one-liner. Man, sort_by{rand} is so awesome! I can’t believe I
didn’t think of that!

Cool! :slight_smile: I have just written this version even shorter:

-pe ‘gsub!(/(\w)(\w+)(?=\w)/){$1+$2.split(//).sort_by{rand}.to_s}’

– fxn

======= End of Original Message =======<

Hi –

···

On Wed, 17 Sep 2003, Tom Felker wrote:

OK, here’s the unscrambler code. Be warned, I chose a rather memory
intensive algorithm, it takes about 110 MB of memory with my 2.4 MB words
file. I don’t know how the Perl I gave works, but it only took about 35
MB. Anyway, it’s very fast after the 5 seconds or so to load the
word list.

You might find the thread at http://www.ruby-talk.org/8142 interesting
in connection with this (“speedup of anagram finder”) .

David


David Alan Black
home: dblack@superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

It’ll work with just sort instead of sort_by, won’t it?
Save three characters! :slight_smile:

-Mark

···

On Wed, Sep 17, 2003 at 03:33:23AM +0900, Kurt M. Dresner wrote:

Try

-pe 'gsub!(/\B\w+\B/){$&.split(//).sort_by{rand}.join}

Shorter (and even cleaner too!)

That looks to me like it will not preserve the first and last letter
in each word, which is a requirement.

Gavin

···

On Wednesday, September 17, 2003, 4:33:23 AM, Kurt wrote:

Try

-pe 'gsub!(/\B\w+\B/){$&.split(//).sort_by{rand}.join}

Shorter (and even cleaner too!)

-Kurt

preserved for eternity @ http://www.rubygarden.org/ruby?OneLiners

robert

“Kurt M. Dresner” kdresner@cs.utexas.edu schrieb im Newsbeitrag
news:20030916183318.GE13330@cs.utexas.edu…

Try

-pe 'gsub!(/\B\w+\B/){$&.split(//).sort_by{rand}.join}

Shorter (and even cleaner too!)

-Kurt

This is hilarious, because a friend and I just had (over lunch) a
race to see who could code it up first. I used Ruby and he used
Perl. Mine worked first, but then I reworked it a bit to make it
neater. His was impossible to read, but I totally have to send him
this one-liner. Man, sort_by{rand} is so awesome! I can’t believe
I

···

On Wed, Sep 17, 2003 at 03:20:53AM +0900, Xavier Noria wrote:

On Tuesday 16 September 2003 20:07, Kurt M. Dresner wrote:

didn’t think of that!

Cool! :slight_smile: I have just written this version even shorter:

-pe ‘gsub!(/(\w)(\w+)(?=\w)/){$1+$2.split(//).sort_by{rand}.to_s}’

– fxn

======= End of Original Message =======<

I don’t /think/ it works with sort. Someone correct me if I’m wrong,
but the block passed to sort has to return either a -1, 0, or 1, and I
think it needs to be consistent between two items (at least it should
be).

-Kurt

···

On Wed, Sep 17, 2003 at 04:12:07AM +0900, Mark J. Reed wrote:

On Wed, Sep 17, 2003 at 03:33:23AM +0900, Kurt M. Dresner wrote:

Try

-pe 'gsub!(/\B\w+\B/){$&.split(//).sort_by{rand}.join}

Shorter (and even cleaner too!)

It’ll work with just sort instead of sort_by, won’t it?
Save three characters! :slight_smile:

-Mark

======= End of Original Message =======<

But it does. the \B only matches a non-word boundary, so the match
can’t include the first and last letter. Secondly, if the word is too
short, it just fails to match at all.

-Kurt

···

On Wed, Sep 17, 2003 at 07:35:11AM +0900, Gavin Sinclair wrote:

On Wednesday, September 17, 2003, 4:33:23 AM, Kurt wrote:

Try

-pe 'gsub!(/\B\w+\B/){$&.split(//).sort_by{rand}.join}

Shorter (and even cleaner too!)

-Kurt

That looks to me like it will not preserve the first and last letter
in each word, which is a requirement.

Gavin

======= End of Original Message =======<

No, not quite. Sort will pass in two args and expect you to return -1, 0
or 1. So you could say

enum.sort { rand(3)-1 }

…or…

enum.sort { rand() <=> 0.5 }

…but I don’t know what evil effects having the comparison be random
will have on a sorting algorithm. Best to stick with sort_by.

Jason Creighton

···

On Tue, 16 Sep 2003 19:04:53 GMT “Mark J. Reed” markjreed@mail.com wrote:

On Wed, Sep 17, 2003 at 03:33:23AM +0900, Kurt M. Dresner wrote:

Try

-pe 'gsub!(/\B\w+\B/){$&.split(//).sort_by{rand}.join}

Shorter (and even cleaner too!)

It’ll work with just sort instead of sort_by, won’t it?
Save three characters! :slight_smile:

Right, right. That occurred to me after I sent that message. For
some reason I was thinking that sort and sort_by were the same method
with two aliases, the longer one intended for use when passing a block.
Obviously the blocks really do different things - the sort_by block
converts each item into a key to be compared, while the sort block
performs the actual comparisons.

Just ignore me. :slight_smile:

-Mark

···

On Wed, Sep 17, 2003 at 04:26:01AM +0900, Kurt M. Dresner wrote:

I don’t /think/ it works with sort. Someone correct me if I’m wrong,
but the block passed to sort has to return either a -1, 0, or 1, and I
think it needs to be consistent between two items (at least it should
be).

But it does. the \B only matches a non-word boundary, so the match
can’t include the first and last letter. Secondly, if the word is too
short, it just fails to match at all.
yeah, it’s one of the prettiest one-liners I’ve seen.
nikolai

···


::: name: Nikolai Weibull :: aliases: pcp / lone-star / aka :::
::: born: Chicago, IL USA :: loc atm: Gothenburg, Sweden :::
::: page: www.pcppopper.org :: fun atm: gf,lps,ruby,php,war3 :::
main(){printf(&linux[“\021%six\012\0”],(linux)[“have”]+“fun”-97);}