18
\$\begingroup\$

Context

In APL, trains are tacit sequences of monadic/dyadic functions that can be called with one or two arguments. We'll code something to check if a given train follows the correct structure we need in order to have a sound train.

Task

Given the sequence of function arities in the train, determine if the train is valid as a monad and/or as a dyad. Don't forget that APL reads from right to left, so when I mention the "start" I mean the end of the array! A train is valid as a monad if

  • is starts with an arbitrary number of DM (0 or more) and then ends in 1 or 2 monadic functions; e.g. MM, MDM, MMDM and MDMDM are valid monadic trains.

A dyadic train is valid if

  • the train starts with an odd number of dyadic functions, possibly ending with a monadic function; e.g. D, MDDD and DDDDD are valid dyadic trains.

Input

Your input is going to be a non-empty list of the arities of the functions in the train, where said list contains up to 3 different elements; one for purely monadic functions, another for purely dyadic functions and another for functions that can be either monadic or dyadic, depending on usage.

The input list can be taken in any sensible format and likewise the elements can be whatever 3 distinct elements you choose. E.g. take a string with the letters MDB or take a list of integers 0,1,2. I don't mind you play around with this, just let us know what your answer uses.

APL reads from right to left and we will embody this in the challenge; input cannot be reversed.

Output

Your function should adhere to one of the two output formats:

  • output one of 4 distinct values; one for a train that only works monadically, one for a train that works dyadically, one for a train that works both ways and yet another one for a train that doesn't work in any way; any consistent 4 distinct values will do;

  • output two Truthy/Falsy values, with respect to the standard Truthy/Falsy defaults of your language, where the first value flags if the train works monadically and the second to flag if the train works dyadically, or vice-versa.

Test cases:

The pair (a, b) is used, where a says if the train is valid to be used monadically and b says if the train is valid dyadically.

DB
(False, False)
DD
(False, False)
DM
(False, False)
MBDBMDD
(False, False)
DDBB
(False, False)
DMMDDM
(False, False)
DBDDBDMMD
(False, False)
BMDBDD
(False, False)
MMMDD
(False, False)
MMBMBMMBM
(False, False)
DDBBMDDMMD
(False, False)
DDMB
(False, False)
D
(False, True)
MD
(False, True)
BD
(False, True)
BBBDBDDBD
(False, True)
MDBBBBDB
(False, True)
M
(True, False)
MM
(True, False)
BM
(True, False)
MMDM
(True, False)
MDM
(True, False)
BDM
(True, False)
MMBBDMDB
(True, False)
MBM
(True, False)
B
(True, True)
MB
(True, True)
BB
(True, True)
BBB
(True, True)
BBBB
(True, True)
BBBBB
(True, True)
MBBBBBBB
(True, True)
BDBBBBBDB
(True, True)

Generated and tested with this Python code. Feel free to use the TIO link and edit the final printing loop to print all the test cases in a format that is easier for you to use in your answer.

\$\endgroup\$
8
  • \$\begingroup\$ Related \$\endgroup\$
    – RGS
    Oct 20, 2020 at 9:49
  • \$\begingroup\$ Related \$\endgroup\$
    – Adám
    Oct 20, 2020 at 9:52
  • 1
    \$\begingroup\$ May we take an integer encoded in base 4? e.g. MDBBBBDB = \$21333313_4\$ = \$40951_{10}\$? \$\endgroup\$
    – Arnauld
    Oct 20, 2020 at 14:14
  • 1
    \$\begingroup\$ @Arnauld I am very curious about what you are coming up with. As of now, the spirit of the input rule is to have programs accept a sequence, where each element in the sequence encodes a valence. A base 4 number in a string, "21333313", or written in base 10 as 21333313 would be acceptable, but encoded as 40951 it no longer has the "each element encodes a function valence" feel and thus would not be acceptable. Does this make sense? \$\endgroup\$
    – RGS
    Oct 20, 2020 at 14:51
  • 1
    \$\begingroup\$ Ok. I have removed my 58-byte version which was using that, since it's not interesting anymore if I have to convert from a base-4 string to decimal. \$\endgroup\$
    – Arnauld
    Oct 20, 2020 at 14:55

7 Answers 7

10
\$\begingroup\$

Haskell, 61 bytes

f t=[and$zipWith(/=)t$[2|even$length t]++cycle[x,0]|x<-[2,0]]

Try it online!

Takes a list of: 0 for monad, 1 for both, 2 for dyad. Returns [a,b].

The valid monad trains are:

M
MM
MDM
MMDM
MDMDM
MMDMDM
...

And the valid dyad trains are:

D
MD
DDD
MDDD
DDDDD
MDDDDD
...

So we check the input against this pattern, first for x=M and then x=D:

x
Mx
xDx
MxDx
xDxDx
MxDxDx
...

To generate the pattern, we start with M if the length is even, then alternate x and D. (Haskell's laziness lets me use cycle, which creates an infinite alternating list, rather than specify how long to alternate for: zipWith will only consume the pattern until it hits the end of t.)

I use (/=) and invert the pattern to support B as a wildcard.

\$\endgroup\$
8
\$\begingroup\$

APL (Dyalog Unicode), 59 bytes (SBCS)

Anonymous tacit prefix function. Returns [] if invalid, [[]] if dyadic, [0] if monadic, and [0,[]] if both.

R←'\pL'⎕R'[&B]'
((R'^M(DM)*M?$')⎕S 3,(R'^D(D{2})*M?$')⎕S⍬)⌽

Try it online!

R← define helper function to expand regexes:
'\pL'⎕R… PCRE Replace characters with the property Letter with…
'[&B] with open-bracket, the letter, "b", close-bracket (makes any letter represent itself or "B")

 reverse the train (to work from right to left)
(,) concatenate the results of applying the following two functions:
 1. ()⎕S 3 PCRE Search for the following regex ([0] if found, else []):
   R'^M(DM)*M?$' the function R applied to the string: ^[MB]([DB][MB])*[MB]?$
 2. ()⎕S⍬ PCRE Search for the following regex ([[]] if found, else []):
   R'^D(D{2})*M?$' the function R applied to the string: ^[DB]([DB]{2})*[MB]?$

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Great choice of language. \$\endgroup\$
    – user
    Oct 22, 2020 at 17:26
  • 2
    \$\begingroup\$ @user Oh, you mean Perl? Yeah. \$\endgroup\$
    – Adám
    Oct 22, 2020 at 19:54
4
\$\begingroup\$

JavaScript (ES6),  67 64  61 bytes

Expects an integer with 123 for MDB. Returns 1 for monadic, 2 for dyadic, 3 for both, or 0 for neither.

f=(n,d=2,q=m=1)=>n<4?q&n|(n^m&&d):f(n/10,d&n%5,n%5&m&&q,m^=3)

Try it online!

How?

We use the following variables:

  • \$n\$ is the input, which is divided by \$10\$ between each iteration. (The result is not rounded to save a few bytes, but the rest of the code was tuned to behave as if it were.)
  • \$d\$ is a flag initialized to \$2\$ and set to \$0\$ as soon as a monadic-only function is extracted, thus breaking a dyadic train.
  • \$q\$ is a flag initialized to \$1\$ and set to \$0\$ as soon as the dyadic/monadic pattern is no longer met, thus breaking a monadic train.
  • \$m\$ is a mask initialized to \$1\$ and switching between \$1\$ and \$2\$ between each iteration. It is used to update \$q\$ and for the final test of the dyadic train.

In order to save two bytes on the update of \$d\$ and \$q\$, we use the fact that:

$$(n\bmod 10) \operatorname{AND} 3 = (n\bmod 5) \operatorname{AND} 3,\:n\bmod 10<4$$

We stop the recursion as soon as there's only one digit left, which is therefore stored in the final value of \$n\$.

The train is monadic if:

\$q\$ is still set to \$1\$ and \$n\$ is odd

The train is dyadic if:

\$d\$ is still set to \$2\$ and \$n\$ is not equal to \$m\$

This last condition (\$n\neq m\$) can be interpreted as follows:

  • If \$m=n=1\$, it means that we had an even number of dyadic functions so far and the last function is monadic-only.
  • If \$m=n=2\$, it means that we had an odd number of dyadic functions so far and the last function is dyadic-only.
\$\endgroup\$
4
\$\begingroup\$

05AB1E, 36 34 30 27 bytes

$DgÈić≠s}WĀs2Å€É}¬_sDÔQ*‚*

Input as a string where D=1,M=0,B=2, output as a pair of truthy/falsey values [isDyadic, isMonadic].

Try it online or verify all test cases.

Explanation:

$                   # Push 1 and the input
 Dg                 # Duplicate the input, and pop and push its length
   Èi               # If this length is even:
     ć              #  Extract head; pop and push remainder-string and head separated
      ≠             #  Check that this head is NOT 1 (thus NOT "D")
       s            #  Swap so the remainder-string is at the top
    }W              # After the if-statement: get the smallest digit (without popping)
      Ā             # Check that this digit is NOT 0 (thus NOT "M") (0 if 0; 1 if 1 or 2)
       s            # Swap so the string is at the top again
        2Å€         # For every 2nd digit (0-based indices 0,2,4,etc.):
           É        #  Replace all 2s with 0s ("B" to "M"), by checking whether the
                    #  digit is odd (1 if 1; 0 if 0 or 2)
          }         # Close the even-map, which changed the string to a digit-list
           Ā        # Replace all remaining 2s with 1s ("B" to "D"), by python-style
                    # truthifying each digit (1 if 1 or 2; 0 if 0)
            ¬       # Get the first digit (without popping the list itself)
             _      # Check that it's equal to 0 (thus "M")
            s       # Swap so the list is at the top again
             D      # Duplicate this list
              Ô     # Connected uniquify; remove any digits equal to its neighbor
               Q    # Check that the two lists are equal (thus it was alternating)
                *   # Multiply it by the head=="M"
                 ‚  # Pair it together with the minimum!="M"
                  * # And multiply both to the head!="D"
                    # or the 1 we pushed initially with `$` if the length was odd
                    # (after which the result is output implicitly)
\$\endgroup\$
3
\$\begingroup\$

Jelly, 26 25 bytes

LḂ
¹;2ṙ1ƊÇ?µnJḂ$Ạð,µḢnÇaẠ

Try it online!

Input is a list of numbers: 0 = M, 1 = D, 2 (or any other number) = B. Output is a pair of 0 or 1, representing the possibility of a monad/dyad respectively.

I really wanted to beat the 05AB1E submission and it seems that I succeeded.

Explanation

LḂ   Auxiliary link
L    Length
 Ḃ   Parity

¹;2ṙ1ƊÇ?µnJḂ$Ạð,µḢnÇaẠ   Main link accepting a list L
       ?                 If
      Ç                  previous link (the length of L is odd)
¹                        then do nothing
     Ɗ                   else (
 ;2                        Append 2
   ṙ1                      Rotate left by 1
                         )
        µ                Now we have a new list L'
          JḂ$            The parities of indices of L' ([1,0,1,0,...])
         n               Doesn't equal L' (for each element)
             Ạ           All?
              ð,         Pair this all with
                µ        the following:
                 Ḣ       Head of L [removing it from L]
                  n      Doesn't equal
                   Ç     previous link (the parity of the length of [the new] L)
                    a    And
                     Ạ   all elements of [the new] L are truthy

-1 byte by extracting an auxiliary link. Sometimes you need to follow good coding practices even in code golf…

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Hey there and welcome. I must say I'm impressed! Good job! \$\endgroup\$
    – RGS
    Nov 8, 2020 at 23:34
2
\$\begingroup\$

Retina 0.8.2, 50 bytes

^(?=(\S\S?(\D\S)*$))?(?=(\S?\D(\D\D)*$))?.*
$#1$#3

Try it online! Takes input as a string of characters where # represents B, represents D and 0 represents M, however the link's header translates BDM for you if necessary. Output is a pair of bits. Explanation:

(?=(\S\S?(\D\S)*$))?

Try to match a monadic chain.

(?=(\S?\D(\D\D)*$))?

Try to match a dyadic chain.

^.*
$#1$#3

Replace the input with the results of the two match attempts.

\$\endgroup\$
1
\$\begingroup\$

APL (Dyalog Unicode), 49 bytes

'MD'∘.{''≡(∊'^[MB]?(['⍺'B][DB])*['⍺'B]$')⎕R''⊢⍵}⊂

Try it online!

Testing harness borrowed from Adám's answer, which you should upvote.

Outputs [0, 0] for invalid trains, [0, 1] for dyadic trains, [1, 0] for monadic trains, and [1, 1] for trains that could be both. This basically just checks if it matches the regex /^[MB]?([aB][DB])*[aB]$/, where a is M or D.

\$\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.