23
\$\begingroup\$

Introduction

The Parsons code is just a simple way to describe pitch variations in a piece of music, whether a note is higher or lower than the previous one.

Even if you suck at remembering tunes, you can still pretty much remember if a note goes up or down, thus the Parsons code can help you to identify a music using a search engine.


Description

Each variation is represented by a single character, which is one of the following:

  • R if the note is the same than the previous one (stands for "Repeat")
  • U if the note is higher than the previous one (stands for "Up")
  • D if the note is lower than the previous one (stands for "Down")

The initial note is written as *.


Example

Here is an example of Parsons code (beginning of "Ode to Joy"):

*RUURDDDDRUURDR

You can actually visualize it, like this:

      *-*                    
     /   \                   
    *     *                  
   /       \                 
*-*         *         *-*    
             \       /   \   
              *     *     *-*
               \   /         
                *-*          

We'll call that a contour from now on.

The rules for drawing such countours are considered self-explained by the above example.



Challenge

Now comes the real challenge.

Write a program that, given a contour as input, outputs its corresponding Parsons code.

You are not asked to draw the contour, but the opposite actually.
From the contour, find the original Parsons code.


Rules

  • The usual rules for code golfing apply
  • The shortest program in number of bytes wins
  • The input is a contour, and the output shall be a valid Parsons code
  • Details about extra whitespace for the input are irrelevant, do whatever works best for you
  • You are not allowed to hardcode, one way or another, parts of the output and / or the program using extra whitespace because of the previous rule

Notes

\$\endgroup\$
10

9 Answers 9

24
\$\begingroup\$

CJam, 21 bytes

qN/:.e>(o2%:i"DRXU"f=

Fold the lines (:) by vectorizing (.) a character-wise maximum operation e>. Since there is only one non-space character in each column, this one will be the result, as space has a smaller ASCII code than all printable non-space characters.

Unshift and print the first asterisk (o, then map every other (2%) remaining char to UDR using modular indexing.

Old solution (29 bytes)

'*qN/z2%'*f#0+2ew);::-"RDU"f=

qN/ gets input lines. z transposes this character matrix. 2% drops every odd row. '*f# finds the index of the asterisk in each row. 0+2ew); gets all successive pairs of indices. ::- computes their differences, and "RDU"f= maps them to letters (via modular indexing: 0 → R, 2 → U, -2 ≡ 1 → D). The leading '* prepends the asterisk.

EDIT: I changed 2ew to 0+2ew); to work around CJam not handling ew (successive slices) on lists that are too short. This makes the code work for the input string *.

Try it here, or watch it in action:

              
\$\endgroup\$
5
  • 7
    \$\begingroup\$ This is really awesome to watch. \$\endgroup\$
    – Jeel Shah
    Dec 29, 2015 at 3:05
  • 2
    \$\begingroup\$ I agree! +1 for the code, wish I could +10 for the GIF. \$\endgroup\$ Dec 29, 2015 at 3:17
  • \$\begingroup\$ BTW, I like the smiley at code length 17 :-p \$\endgroup\$ Dec 29, 2015 at 3:20
  • 1
    \$\begingroup\$ I agree, this is awesome to watch. It does not work with * as input though. I get a nice RuntimeException instead. \$\endgroup\$ Dec 29, 2015 at 10:07
  • \$\begingroup\$ Ugh, I consider that a CJam bug: [X]2ew should return [] instead of erroring out. I'll add a workaround, though. \$\endgroup\$
    – Lynn
    Dec 29, 2015 at 16:48
4
\$\begingroup\$

Pyth - 28 25 27 25 bytes

2 byes saved thanks to @Jakube.

s+\*@L"RDU"-VFtBxR\*%2C.z

Try it online here.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ It does not work with * as input. The output is *0 while it should be just *. An evil wild trailing zero appeared. \$\endgroup\$ Dec 29, 2015 at 10:11
  • \$\begingroup\$ @HelgevonKoch fixed \$\endgroup\$
    – Maltysen
    Dec 29, 2015 at 19:46
  • \$\begingroup\$ You've swapped U and D by the way. Otherwise, well done. \$\endgroup\$ Jan 2, 2016 at 11:06
  • \$\begingroup\$ @HelgevonKoch oops \$\endgroup\$
    – Maltysen
    Jan 2, 2016 at 20:01
4
\$\begingroup\$

Python 3, 129 108 98 86 bytes

There are probably several ways to golf this, but I rather like that I got it all down to one line.

Edit: Now using ''.translate()

Edit: With many thanks to wnnmaw.

Edit: I changed the input format to an array of strings instead of a newline-separated string to save bytes. Also, in the last edit, I mixed up U and R, so I fixed that.

lambda a:'*'+"".join(('UR'[j<'/']+'D')[j>'/']for l in zip(*a)for j in l if j in'-/\\')

Input must be an array of strings. For the example above, this looks something like:

["      *-*                    ","     /   \                   ","    *     *                  ","   /       \                 ","*-*         *         *-*    ","             \       /   \   ","              *     *     *-*","               \   /         ","                *-*          "]

Ungolfed:

def f(a):
    s = ''
    for c in zip(*a):           # transpose
        for d in c:             # for each letter in column c
            if e in "-/\\":     # if that letter is either -,/,\
                if e < '/':     # if < '/' (same as if == '-')
                    s += "R"
                elif e > '/':   # if > '/' (same as if == '\')
                    s += "D"
                else:           # if == '/'
                    s += "U"
        return "*" + s          # in the code we ''.join() it all together
                                # in this ungolfing, we add to an empty string
\$\endgroup\$
4
  • \$\begingroup\$ I don't want to make a new answer because I borrowed a lot from yours, but lambda s:'*'+"".join((('D','R')[j=='-'],'U')[j=='/']for l in zip(*s.split('\n'))for j in l if j in'\\/-') clocks in at 105 bytes. Main difference is using a tuple conditional instead of translate \$\endgroup\$
    – wnnmaw
    Dec 29, 2015 at 21:59
  • \$\begingroup\$ Thanks @wnnmaw! I think I can golf that down even further! \$\endgroup\$
    – Sherlock9
    Dec 30, 2015 at 3:36
  • \$\begingroup\$ Nice golfing on the conditional in the front, that is super clever! \$\endgroup\$
    – wnnmaw
    Dec 30, 2015 at 14:51
  • \$\begingroup\$ Ah shucks. Thanks for the tip @wnnmaw \$\endgroup\$
    – Sherlock9
    Dec 30, 2015 at 15:39
3
\$\begingroup\$

Ruby, 87 bytes

Requires trailing spaces in the input so that all lines are the same length.

$><<?*+$<.readlines.map(&:chars).transpose.join.gsub(/./,{?-=>:R,?/=>:U,?\\=>:D}).strip
\$\endgroup\$
1
  • 4
    \$\begingroup\$ Surely this is malicious code, as it contains >:D. \$\endgroup\$
    – Alex A.
    Dec 29, 2015 at 3:32
3
\$\begingroup\$

Japt, 38 bytes 40 41 45 46 48

Saved 2 bytes thanks to @ETHproductions

'*+U·y £Yu ?"RUD"g1+(XrS c -47 g):P} q

If there was a trim command this would be only 38 bytes ;-; will add explanation when I'm done golfing. The :P is not the program trying to be funny, it's actually the program ignoring characters that aren't important.

Try it online

\$\endgroup\$
4
  • \$\begingroup\$ When I saw that there was a challenge practically requiring an array transpose, and that Doᴡɴɢᴏᴀᴛ had answered it, I knew it had to be Japt. \$\endgroup\$ Dec 29, 2015 at 4:00
  • \$\begingroup\$ BTW, I'll add in a trim function at x and transpose and rotate functions at y and z (split at newlines, use the array function, join with newlines) \$\endgroup\$ Dec 29, 2015 at 13:36
  • \$\begingroup\$ You could save two bytes like this: Yu ?"RUD"g1+(XrS c -47 g):P \$\endgroup\$ Dec 29, 2015 at 13:40
  • \$\begingroup\$ @ETHproductions thanks, down to under 40 bytes! \$\endgroup\$
    – Downgoat
    Dec 29, 2015 at 19:25
3
\$\begingroup\$

Haskell, 89 bytes

import Data.List
m '/'="U"
m '-'="R"
m '\\'="D"
m _=""
('*':).(>>=(>>=m)).transpose.lines

Usage example:

*Main> ('*':).(>>=(>>=m)).transpose.lines $ "      *-*                    \n     /   \\                   \n    *     *                  \n   /       \\                 \n*-*         *         *-*    \n             \\       /   \\   \n              *     *     *-*\n               \\   /         \n                *-*          "
"*RUURDDDDRUURDR"

*Main> ('*':).(>>=(>>=m)).transpose.lines $ "*"
"*"

Transpose the input and replace the characters //-/\ with singleton strings "U"/"R"/"D". All other chars are replaced by empty strings "", which later disappear by concatenating everything. Finally, prepend the asterisk *.

\$\endgroup\$
2
\$\begingroup\$

Mathematica, 103 bytes

"*"<>(Differences@Position[Thread@Characters@StringSplit[#,"
"],"*"][[;;,2]]/.{-2->"U",0->"R",2->"D"})&

Quite short, considering that this is a string-processing challenge.

\$\endgroup\$
2
\$\begingroup\$

JavaScript (ES6) 90

An anonymous function. It scans the input string char by char, taking into accout the position in the current line. Doing this, it builds an output array subsituting U D R for / \ - at the right place

c=>[...c].map(c=>c>'*'?t[i++]=c>'/'?'D':c<'/'?'R':'U':c<' '?i=0:++i,t=['*'],i=0)&&t.join``
\$\endgroup\$
2
\$\begingroup\$

Matlab, 62 bytes

r=@(s)[85-(s<14)*3-(s>59)*17,''];@(p)r(sum(p(:,2:2:end)-32))

This requires the input to be a rectangular (same number of characters in each row). E.g.

    ['      *-*                    ';    '     /   \                   ';    '    *     *                  ';    '   /       \                 ';    '*-*         *         *-*    ';    '             \       /   \   ';    '              *     *     *-*';    '               \   /         ';    '                *-*          '];

Explanation

sum(p(:,2:2:end)-32)        % exctract every second column, substract 32 (spaces->zeros) 
                            % and sum column wise (results in a vector of 3 different values)
[85-(s<14)*3-(s>59)*17,'']  % map each of the values to the corresponding value of the letter and convert back to characters
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.