LinuxQuestions.org
Visit the LQ Articles and Editorials section
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices

Reply
 
Search this Thread
Old 03-21-2012, 02:57 PM   #1
danielbmartin
Senior Member
 
Registered: Apr 2010
Location: Apex, NC, USA
Distribution: Ubuntu
Posts: 1,094

Rep: Reputation: 288Reputation: 288Reputation: 288
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
 
Old 03-21-2012, 04:36 PM   #2
millgates
Member
 
Registered: Feb 2009
Location: 192.168.x.x
Distribution: Slackware
Posts: 651

Rep: Reputation: 269Reputation: 269Reputation: 269
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.
 
1 members found this post helpful.
Old 03-22-2012, 09:07 AM   #3
danielbmartin
Senior Member
 
Registered: Apr 2010
Location: Apex, NC, USA
Distribution: Ubuntu
Posts: 1,094

Original Poster
Rep: Reputation: 288Reputation: 288Reputation: 288
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
 
Old 03-22-2012, 09:27 AM   #4
danielbmartin
Senior Member
 
Registered: Apr 2010
Location: Apex, NC, USA
Distribution: Ubuntu
Posts: 1,094

Original Poster
Rep: Reputation: 288Reputation: 288Reputation: 288
[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
 
Old 03-22-2012, 09:59 AM   #5
millgates
Member
 
Registered: Feb 2009
Location: 192.168.x.x
Distribution: Slackware
Posts: 651

Rep: Reputation: 269Reputation: 269Reputation: 269
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
 
1 members found this post helpful.
Old 03-22-2012, 10:09 AM   #6
danielbmartin
Senior Member
 
Registered: Apr 2010
Location: Apex, NC, USA
Distribution: Ubuntu
Posts: 1,094

Original Poster
Rep: Reputation: 288Reputation: 288Reputation: 288
Quote:
Originally Posted by millgates View Post
how about this?
Code:
rev $InFile
This is clearly the best. Thank you again for educating me.

Daniel B. Martin
 
Old 03-22-2012, 12:08 PM   #7
grail
Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 7,508

Rep: Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893
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
 
1 members found this post helpful.
Old 10-25-2012, 09:19 AM   #8
danielbmartin
Senior Member
 
Registered: Apr 2010
Location: Apex, NC, USA
Distribution: Ubuntu
Posts: 1,094

Original Poster
Rep: Reputation: 288Reputation: 288Reputation: 288
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
 
Old 10-25-2012, 09:50 AM   #9
grail
Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 7,508

Rep: Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893
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}
 
1 members found this post helpful.
Old 10-25-2012, 10:14 AM   #10
danielbmartin
Senior Member
 
Registered: Apr 2010
Location: Apex, NC, USA
Distribution: Ubuntu
Posts: 1,094

Original Poster
Rep: Reputation: 288Reputation: 288Reputation: 288
Quote:
Originally Posted by grail View Post
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
 
Old 10-25-2012, 10:48 AM   #11
grail
Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 7,508

Rep: Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893
You probably have a pre 4+ version of gawk, so add --re-interval.
 
1 members found this post helpful.
Old 10-25-2012, 12:59 PM   #12
danielbmartin
Senior Member
 
Registered: Apr 2010
Location: Apex, NC, USA
Distribution: Ubuntu
Posts: 1,094

Original Poster
Rep: Reputation: 288Reputation: 288Reputation: 288
Quote:
Originally Posted by grail View Post
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
 
Old 10-26-2012, 10:07 AM   #13
danielbmartin
Senior Member
 
Registered: Apr 2010
Location: Apex, NC, USA
Distribution: Ubuntu
Posts: 1,094

Original Poster
Rep: Reputation: 288Reputation: 288Reputation: 288
Quote:
Originally Posted by grail View Post
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
 
Old 10-26-2012, 01:00 PM   #14
grail
Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 7,508

Rep: Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893Reputation: 1893
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
 
1 members found this post helpful.
Old 10-26-2012, 01:36 PM   #15
danielbmartin
Senior Member
 
Registered: Apr 2010
Location: Apex, NC, USA
Distribution: Ubuntu
Posts: 1,094

Original Poster
Rep: Reputation: 288Reputation: 288Reputation: 288
Quote:
Originally Posted by grail View Post
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
 
  


Reply

Tags
awk, sed, text processing


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
[SOLVED] Block Matrices in C ejspeiro Programming 7 02-13-2012 05:37 PM
Bash scripting: parsing a text file character-by-character Completely Clueless Programming 13 08-12-2009 09:07 AM
To know the function on checking whether a character is ascii or unicode character. murugesan Programming 2 01-23-2009 01:07 PM
<fun> The Windows Crash </fun> Simon Bridge General 6 08-26-2007 07:46 PM


All times are GMT -5. The time now is 04:50 AM.

Main Menu
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration