LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (http://www.linuxquestions.org/questions/programming-9/)
-   -   Fun with character matrices (http://www.linuxquestions.org/questions/programming-9/fun-with-character-matrices-935747/)

danielbmartin 03-21-2012 03:57 PM

Fun with character matrices
 
I'm playing with character matrices and learning how to manipulate them.

Example 1, Horizontal rotation. Take a character matrix and rotate each row right by r where r is the row index minus 1. This transformation might be called a "skew" or "twist."

If the input file is ...
Code:

abcde
fghij
klmno
pqrst

... then the output file is...
Code:

abcde
jfghi
noklm
rstpq

I accomplished the transformation with this code ...
Code:

sed 's/.*/&&/' $InFile \
|awk -F "" '{print substr($0,(NF/2+2)-NR,NF/2)}'


Example 2, Vertical rotation. Do a similar "twist" on columns.
Again, the input file is ...
Code:

abcde
fghij
klmno
pqrst

... then the desired output file would be...
Code:

aqmie
fbrnj
kgcso
plhdt

I tried to construct a solution based on the successful horizontal rotation code (above) but without success.

Ideas? Suggestions?

Daniel B. Martin

millgates 03-21-2012 05:36 PM

Code:

sed 's/.*/&&/' $InFile \
|awk -F "" '{print substr($0,(NF/2+2)-NR,NF/2)}'

Why the sed part? Why don't you duplicate the line in the awk command?

Code:

awk -F "" '{print substr($0$0,(NF+2)-NR,NF)}' $InFile
You could do the vertical version by transposing the matrix first, then using the first code and transposing it again:
Code:

awk -F "" '{ for (i=1; i<=NF;i++) arr[i]=arr[i]$i } \
    END { i=1; while (i in arr) { print arr[i]arr[i]; i++; }}' $InFile |
awk -F "" '{print substr($0,(NF/2+2)-NR,NF/2)}' |
awk -F "" '{ for (i=1; i<=NF;i++) arr[i]=arr[i]$i } \
    END { i=1; while (i in arr) { print arr[i]; i++; } }'

Maybe there is a nicer solution.

danielbmartin 03-22-2012 10:07 AM

Thank you, millgates, for your constructive response.

Code:

Why the sed part? Why don't you duplicate the line in the awk command?
Only one reason: newbie ignorance.

Quote:

You could do the vertical version by transposing the matrix first, then using the first code and transposing it again ...
I considered doing it that way but was reluctant to use three steps when (in my imagination) there was a one-awk solution.

I like your transpose code.
Code:

awk -F "" '{ for (i=1; i<=NF;i++) arr[i]=arr[i]$i } \
    END { i=1; while (i in arr) { print arr[i]; i++; }}' $InFile

Why do you use the backslash? Is it a matter of personal coding style? The awk produces a correct result with or without it.

Daniel B. Martin

danielbmartin 03-22-2012 10:27 AM

[QUOTE=millgates;4632981]
Code:

awk -F "" '{print substr($0$0,(NF+2)-NR,NF)}' $InFile
To repeat: I like your awk for transpose. Please, if possible, modify it to do another matrix manipulation.

Rotate a matrix about its vertical axis.
This input...
Code:

abcde
fghij
klmno
pqrst

... produces this result...
Code:

edcba
jihgf
onmlk
tsrqp

I use this code which was found at http://www.linuxhowtos.org/System/sedoneliner.htm.
Code:

sed '/\n/!G;s/\(.\)\(.*\n\)/&\2\1/;//D;s/.//' $InFile
This is a dense, difficult-to-read, three-step sed. Perhaps you can do a better awk implementation.

Daniel B. Martin

millgates 03-22-2012 10:59 AM

Quote:

Originally Posted by danielbmartin
I considered doing it that way but was reluctant to use three steps when (in my imagination) there was a one-awk solution.

Ok, if you want a single awk command, this might work:

Code:

awk -F "" '{ file[NR] = $0 } END {
    for (i = 1; i <= NR; i++) {
        foo = "";
        for (j= 1; j <= length(file[1]); j++) {
            foo = foo substr(file[(NR + i - j) % NR + 1], j, 1) };
            print foo
        }
    }' $InFile

Quote:

Originally Posted by danielbmartin
Why do you use the backslash? Is it a matter of personal coding style? The awk produces a correct result with or without it.

I guess this is just my share of newbie ignorance :) I'm used to use backslash whenever I break lines in bash even if that's not necessary.

Quote:

Originally Posted by danielbmartin
Rotate a matrix about its vertical axis

how about this?
Code:

rev $InFile
If you insist on using awk, though:
Code:

awk -F "" '{ foo=""; for (i = 1; i <= NF; i++) foo=$i foo; print foo }' $InFile

danielbmartin 03-22-2012 11:09 AM

Quote:

Originally Posted by millgates (Post 4633635)
how about this?

Code:

rev $InFile
This is clearly the best. Thank you again for educating me.

Daniel B. Martin

grail 03-22-2012 01:08 PM

Definitely cannot do better than rev, but here is a slightly shorter awk:
Code:

awk -F "" '{for(i=NF;i>=1;i--)printf "%s",$i ((i==1)?"\n":"")}' file

danielbmartin 10-25-2012 10:19 AM

To continue (after a hiatus) the fun with character matrices...

I want to create a matrix of characters such as ...
Code:

abcde
eabcd
deabc
cdeab

I want the code to be general. The number of columns, number of rows, and the matrix content should be parameters. I came up with this ...
Code:

Nrows=4
Ncols=5
AL='abcdefghijklmnopqrstuvwxyz'
yes ${AL:0:$Ncols}  \
|head -$Nrows      \
|awk -F "" '{print substr($0$0,(NF+2)-NR,NF)}'

... which works.

Intuition suggests this could be accomplished with a single awk. I've struggled with code of this nature ...
Code:

Nrows=4
Ncols=5
AL='abcdefghijklmnopqrstuvwxyz'
awk -v Ncols="$Ncols" -v Nrows="$Nrows" -v AL="$AL"  \
    'BEGIN{for (i=1; i<=Nrows; i++) {print ?????}'

... without success.

awk gurus, please advise.

Daniel B. Martin

grail 10-25-2012 10:50 AM

Well you will probably need more coding to cater for the situation where row > col, but as a start:
Code:

awk '{for(i=1;i <= Nrows;i++)print gensub("(.*)(.{"i"})$","\\2\\1",1)}' Nrows=$Nrows <<<${AL:0:Ncols}

danielbmartin 10-25-2012 11:14 AM

Quote:

Originally Posted by grail (Post 4814658)
Well you will probably need more coding to cater for the situation where row > col, but as a start:
Code:

awk '{for(i=1;i <= Nrows;i++)print gensub("(.*)(.{"i"})$","\\2\\1",1)}' Nrows=$Nrows <<<${AL:0:Ncols}

Thanks for the prompt response. I used your proposed solution this way ...
Code:

echo "Method of LQ Guru grail"
Nrows=4
Ncols=5
AL='abcdefghijklmnopqrstuvwxyz'
awk '{for(i=1;i <= Nrows;i++)print gensub("(.*)(.{"i"})$","\\2\\1",1)}' Nrows=$Nrows <<<${AL:0:Ncols}

... and it produced this ...
Code:

abcde
abcde
abcde
abcde

This output has the desired shape (5x4) and the desired content (abcde) but lacks the "twist."

Daniel B. Martin

grail 10-25-2012 11:48 AM

You probably have a pre 4+ version of gawk, so add --re-interval.

danielbmartin 10-25-2012 01:59 PM

Quote:

Originally Posted by grail (Post 4814701)
You probably have a pre 4+ version of gawk, so add --re-interval.

Yes, my machine has GNU Awk 3.1.6.

Following your suggestion I ran this ...
Code:

echo "Method of LQ Guru grail"
Nrows=4
Ncols=5
AL='abcdefghijklmnopqrstuvwxyz'
awk --re-interval '{for(i=1;i <= Nrows;i++)print gensub("(.*)(.{"i"})$","\\2\\1",1)}' Nrows=$Nrows <<<${AL:0:Ncols}

... and it produced this ...
Code:

eabcd
deabc
cdeab
bcdea

I tweaked the awk "for" loop code to be ...
Code:

echo "Method of LQ Guru grail, modified"
Nrows=4
Ncols=5
AL='abcdefghijklmnopqrstuvwxyz'
awk --re-interval '{for(i=0;i <= Nrows-1;i++)print gensub("(.*)(.{"i"})$","\\2\\1",1)}' Nrows=$Nrows <<<${AL:0:Ncols}

... and it produces this ...
Code:

abcde
eabcd
deabc
cdeab

... which is the desired result.

Thank you!

Daniel B. Martin

danielbmartin 10-26-2012 11:07 AM

Quote:

Originally Posted by grail (Post 4814658)
Well you will probably need more coding to cater for the situation where row > col, but as a start:
Code:

awk '{for(i=1;i <= Nrows;i++)print gensub("(.*)(.{"i"})$","\\2\\1",1)}' Nrows=$Nrows <<<${AL:0:Ncols}

Your excellent solution was easy enough to test... but not so easy for an inexperienced programmer to understand.

I tried to piece it apart and am stumped by "<<<". It's not in my Dougherty & Robbins "sed & awk" book. The search facility of LQ seems to misunderstand <<< as a search field. Google doesn't do any better, so my efforts at self-education are unsuccessful. Therefore I must ask for explanation, elaboration, and pointers to relevant documentation.

Daniel B. Martin

grail 10-26-2012 02:00 PM

That would be because it is a shell (specifically bash) construct known as a here string, as opposed to a here document:

http://mywiki.wooledge.org/HereString

danielbmartin 10-26-2012 02:36 PM

Quote:

Originally Posted by grail (Post 4815626)

Thank you for this pointer.

A side question:
Is it possible to search LQ for a string of special characters such as <<< ?
Is it possible to tell Google to search on such strings?

Daniel B. Martin


All times are GMT -5. The time now is 05:14 AM.