http://www.perlmonks.org?node_id=67973

Here's the goal: make a function that takes any number of equal-length strings, and returns an array of strings, where each string is the concatenation of the Nth character in each string.

Sample input: qw( jeff john mary )
Sample output: qw( jjm eoa fhr fny )

Here are my attempts:

# 51 chars sub s2a{@_=@_;reverse+map{join"",map+chop,@_}1..length$_[0]} # 51 chars sub s2a{@_=@_;map{join"",map+s/(.)//s&&$1,@_}1..length$_[0]} # 48 chars sub s2a{map{join"",map+s/(.)//s&&$1,@_=@_}1..length$_[0]} # 47 chars sub s2a{map{join"",map{s/(.)//s;$1}@_=@_}1..length$_[0]} # 45 chars sub s2a{map{join"",map{s/.//s;$&}@_=@_}1..length$_[0]}


japhy -- Perl and Regex Hacker

Replies are listed 'Best First'.
Re: (Golf) Strings-to-Array
by tye (Sage) on Mar 29, 2001 at 02:34 UTC

    If you allow me my favorite tool, then I can do it in 31 characters and I don't need to use the awful $& and I don't require that the strings be the same length:

    use mapcar; sub s2a{mapcar{join"",@_}map[/(.)/g],@_}

            - tye (but my friends call me "Tye")
Re (tilly) 1: (Golf) Strings-to-Array
by tilly (Archbishop) on Mar 30, 2001 at 09:17 UTC
    I had to rename the function, but I get 38. It would be shorter, but I added 3 chars to make it run under 5.6, and added 2 more for a bug fix. Plus the obligatory 5 for a private variable.

    So if I was a careless lout, I could claim 28, but I am not...

    sub r {my$s;$s.=chop for@_=@_;$s.0?(&r,$s):()}

    UPDATE
    D'oh. MeowChow is right. I need the private variable.

      If you were a lout, the most you could claim is 33, because a private $s variable is mandatory in the recursive case.

      Though I do believe you've reached par for this course.

      tilly would you care to explain it as well at the risk of me producing this type of code on my next project?

      Jorg

      "Do or do not, there is no try" -- Yoda
        Please don't produce this kind of code except in fun.

        But since you ask, the trick is recursion. Here it is broken out:

        sub r { my $s; $s .= chop for @_ = @_; $s . 0 ? ( &r , $s ) : () }
        and now with better variable names, fewer implicit variables, rewritten loops, that kind of thing:
        sub recursive_s2a { my $str_last; foreach my $str (@_ = @_) { $str_last .= chop($str); } if ($str_last . 0) { return (recursive_s2a(@_) , $str_last ); } else { return; } }
        And now you see that the function creates the last string in the list to produce, tests whether that was the empty string, if it was it returns nothing, if it wasn't it then proceeds by recursion to generate the first strings, and adds the string it produced to that list.
      So if I was a careless lout, I could claim 28, but I am not...
      sub r {my$s;$s.=chop for@_=@_;$s.0?(&r,$s):()}

      Tilly, I have tested your line as follows
      sub r {my$s;$s.=chop for@_;$s?(&r,$s):()} #0 1 2 3 3 #123456789012345678901234567890123 # jeff,john,mary # jjm,eoa,fhr,fny
      on v5.6.1-AS628. and it worked fine.

      I suppose I am missing something, but I dont see what.
      Why are you doing @_=@_ and $s.0??
      Is this an issue for an earlier version of perl? As far as I understand these are redundant ops. When does $s.0 evalute in a boolean sense different to $s? And what is the point of assigning @_ to itself?
      I am confused.

      OTOH it does mean that you can claim 5 less chars, for a total of 33.
      :-)
      Or am I being a lout?

      Yves

        In Perl 5.6.0 if you passed in literal strings, you got an error about, "Modification of a read-only value attempted". Recopying @_ fixed that.

        And the time when $s and $s.0 are different in boolean context is when $s is "0". So when you remove those two characters, you get a bug.

        UPDATE
        An example that shows the bug:

        print map "$_\n", r("0a1", "b0", "0");
Re: (Golf) Strings-to-Array
by MeowChow (Vicar) on Mar 30, 2001 at 07:42 UTC
    # 33 chars sub s2a{map{$l[-1+pos].=$&while/./g}@_;@l}
    ... or 38 chars, if you want to throw in my@l;
       MeowChow                                   
                   s aamecha.s a..a\u$&owag.print
      Add 6 chars. 5 for my@l; and one for the s modifier on your regex. 39 looks like the score to beat.

      japhy -- Perl and Regex Hacker
Re: (Golf) Strings-to-Array
by MrNobo1024 (Hermit) on Mar 29, 2001 at 03:50 UTC
    sub s2a{my@q;$d=0,map$q[$d++].=$_,/./sg for@_;@q}
      Ooh, very nice. But it clobbers a global $d. Here's my remedy:
      # 41 chars sub s2a {my@q;map{my$d;map$q[$d++].=$_,/./sg}@_;@q}
      It uses map() in void context, but who cares?

      japhy -- Perl and Regex Hacker
Re: (Golf) Strings-to-Array
by MeowChow (Vicar) on Mar 30, 2001 at 23:21 UTC
    Not really golf, just having fun (requires Math::Matrix):
    sub s2a {map{join'',@$_}@{+Math::Matrix::transpose[map[split''],@_]}}