18
\$\begingroup\$

This is the inverse of negative seven's question.

Write a program or function which, given any single, possibly-empty string of printable ASCII (codes \$[32,126]\$) outputs or returns two strings of printable ASCII.

For any two ordered, possibly empty strings \$s_1\$ and \$s_2\$, there must be third string \$s_0\$, which input to your program, outputs \$s_1\$ and \$s_2\$.


In other words, if \$\mathbb S\$ is the set of all ASCII strings (including the empty string):

\$f\$ is well-defined on \$\mathbb S\$: \$(\forall s\in\mathbb S)(f(s)\in\mathbb S\times\mathbb S)\$

\$f\$ is a surjection to \$\mathbb S\times\mathbb S\$: \$(\forall s_1,s_2\in\mathbb S)(\exists s_0\in\mathbb S)(f(s_0)=(s_1,s_2))\$

Test cases:

For each of these strings (surrounding «quotes» excluded), your output should be two strings:

«»
«hello world»
«'one' 'two' 'three' 'four'»
«"one" "two" "three" "four"»
«["one", "two", "three"]»
«; exit()»

For each of these pairs of strings, there must be at least one string you can provide to your program such that that pair is output (surrounding «quotes» excluded):

 s_1     |  s_2
---------|----------
«hello » | «world»
«hell»   | «o world»
«hello»  | « world»
« »      | «»
«»       | « »
«»       | « !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~»
«\r\n»   | «\r\n»

As always, you are encouraged to add test cases pertinent to your method.

Rules:

  • Input must be a single string/character array/byte array consisting of only ASCII characters.
  • Output is ordered; \$(a,b)\neq(b,a)\$. Output can be any ordered structure in your language, or printed with a newline (LF or CR LF) separator and optional trailing newline.
  • I/O must be consistent. If one case handles input as an argument and outputs to stdout with a trailing newline, all cases must handle input as an argument and output to stdout with a trailing newline.
  • Standard loopholes are forbidden.
  • This is , so shorter programs are better!

As a reminder, this is only a surjection, not a bijection: Your program/function will likely map many different strings to the same output.

\$\endgroup\$
0

9 Answers 9

9
\$\begingroup\$

Python 2, 42 bytes

lambda s:(s.split("1"+len(s)/2*"0")*2)[:2]

Try it online!

The inverse is:

def F(a,b):
    return a+"1"+len(a+b)*"0"+b

A tempting idea for the inverse is to encode the pair of input strings by putting some separator string in between them. That way, we can extract the pair by splitting on this separator. The issue is that the separator may appear in the inputs string.

We get around this by making the separator longer than the total length of both strings. We use 1 followed by a number of 0's equal to the number of characters in both strings. For example,

abc, defgh -> abc100000000defgh

Note that no matter how many 0's and 1's are in the two inputs, there's no way to get another copy of the separator to appear.

We can recover the separator from the combined string by using a number of zeroes equal to half its length rounded down. I had wanted to compute this as 10**_, but Python's repr appends an annoying L to large numbers, which messes it up.

We need to the submission function still give two outputs when the input string doesn't contain the required separator. In this case, splitting just gives the original string, but we copy it twice to be compliant.


Python 2, 35 bytes (not working)

lambda s:s.split(s[:len(s)/3])[-2:]

Try it online!

This would work except it gives an error for the empty string. The idea is to have the inverse encode a,b -> sep + a + sep + b, where the separator sep has the same length as a+b so we can extract it as the first third of the string. A similar idea of putting the separator interspersed runs into the same issue.


Python 2, 43 bytes

lambda s:[s[i::2].lstrip()[1:]for i in 0,1]

Try it online!

The idea for encoding is to intersperse the two input strings with alternating characters. The problem there is that they may have different lengths. So, we pad them to the same length by prepending each one with some number of leading spaces then | (any non-space char will do). For example, abcd, ef goes to

|abcd
  |ef

This can be undone for each string by stripping off all leading spaces then removing the first character. Note that any spaces or | in the actual strings are left unharmed.

Python 3.8 (pre-release), 44 bytes

lambda s:(s[(i:=s.find('_'))+1:2*i],s[2*i:])

Try it online!

Inverse:

F=lambda a,b:(len(a)+1)*"?"+"_"+a+b

For instance, F("ab","cdef") = ???_abcdef. The number of symbols before the _ tells us where to split the remainder -- it's one more than the length of the first string.

We need the submission not to error when there's no _, but this works out because Python's string find uses -1 for missing, which still gives valid slices.

\$\endgroup\$
4
  • \$\begingroup\$ Your 35 byte solution that "Doesn't work" just has an off by one error. Your separator strings are of length 1+|a|+|b|, which is equal to |s|/3+1, since |s|=3|a|+3|b|+2. So if you change that solution to lambda s:s.split(s[:len(s)/3+1])[-2:], just adding a +1, it works fine, for 37 bytes. (Unless I'm missing something?) Try it online! \$\endgroup\$
    – isaacg
    Dec 14, 2019 at 0:50
  • \$\begingroup\$ @isaacg Nice find, that does work. And [1:] for [-2:] saves another byte -- I believe the split is always exactly 3 elements. You can post an answer for the bounty. \$\endgroup\$
    – xnor
    Dec 14, 2019 at 7:21
  • \$\begingroup\$ On more thought, I realised this still doesn't work - f(F('','')) is fixed, but f('') still errors. It'll take more work to fix that. I'll keep looking. \$\endgroup\$
    – isaacg
    Dec 14, 2019 at 18:28
  • \$\begingroup\$ I couldn't find any way to fix the empty string problem in under 6 bytes, making it not an improvement: Try it online! \$\endgroup\$
    – isaacg
    Dec 14, 2019 at 18:38
4
\$\begingroup\$

Haskell, 38 bytes

f(' ':a:b)=(a:)<$>f b
f s=(drop 1s,"")

Try it online!

Not using built-in parsing.


21 bytes (doesn't quite work)

last.(("",""):).reads

Try it online!

(As pointed out by @benrg, this produces unprintable characters outside the valid range on some inputs like "\1" that decode to escape characters.)

The inverse is

g(a,b)=show a++b

This puts the first string in quotes, escaping any characters as needed, and appends the second string. This is undone by reads, which looks for a prefix that's a string representation of the correct type (here, string), reads it, and puts it in a pair with the remainder.

To make the function total, if this fails, we just return two empty strings. Note that reads only does lexing, not evaluation, so it won't mess up on weird inputs.

If we may output a singleton list of a two-string pair, we can save 3 bytes:

18 bytes

max[("","")].reads

Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ Unfortunately the reads solutions don't work because, for instance, the input "\"\\12345\"" contains only printable ASCII but the output contains a non-ASCII character. It's a shame because they're nicely golfed. \$\endgroup\$
    – benrg
    Oct 21, 2019 at 8:13
  • \$\begingroup\$ @benrg Good find. I had tried testing with backslashes but hadn't though about escapes like that. \$\endgroup\$
    – xnor
    Oct 22, 2019 at 7:25
3
\$\begingroup\$

Python 2, 53 52 bytes

lambda s:(s.split('_x'+s.count('x')/2*'x')+[''])[:2]

Try it online!

\$\endgroup\$
3
  • \$\begingroup\$ Excellent! Thanks for providing an inverse function as well. \$\endgroup\$ Oct 20, 2019 at 1:52
  • \$\begingroup\$ @GammaFunction: The inverse function was the Motivator! \$\endgroup\$
    – Chas Brown
    Oct 20, 2019 at 3:31
  • \$\begingroup\$ How do I produce the pair "First_String", "Second_String"? \$\endgroup\$
    – Taemyr
    Oct 21, 2019 at 9:01
3
\$\begingroup\$

05AB1E, 8 7 bytes

g2÷°¡2∍

-1 byte thanks to @Grimy.

Try it online or verify all test cases.

Inverse (4 bytes):

Jg°ý

Try it online or verify all test cases.

Port of @xnor's first Python 2 approach, so make sure to upvote him as well!!

Explanation:

g        # Get the length of the (implicit) input-string
 2÷      # Integer-divide it by 2
   °     # Take 10 to the power this
    ¡    # And split the (implicit) input-string by this integer
     2∍  # Extend this list to (or keep it at) size 2
         # (after which this pair is output implicitly as result)

J        # Join the strings in the (implicit) input-list together to a single string
 g       # Get the length of this
  °      # Take 10 the power this
   ý     # And join the strings of the (implicit) input-list with this as delimiter
         # (after which the resulting string is output implicitly as result)
\$\endgroup\$
3
  • \$\begingroup\$ What a horrific language! +1 \$\endgroup\$ Oct 21, 2019 at 17:52
  • 1
    \$\begingroup\$ Þ2£ -> 2∍ (: \$\endgroup\$
    – Grimmy
    Oct 28, 2019 at 15:50
  • \$\begingroup\$ @Grimmy Now that I see it I don't understand why I used Þ like that in the first place, haha. First time I use it, even though I've used at least two dozen times. Thanks! \$\endgroup\$ Oct 28, 2019 at 20:12
2
\$\begingroup\$

Retina 0.8.2, 33 bytes

^(("[^"]*")*),?
$1¶
%`^"|"("?)
$1

Try it online! Link includes test cases. Takes as input the output from my Charcoal answer to @negativeseven's question and outputs the two strings on separate lines. Explanation:

^(("[^"]*")*),?
$1¶

My Charcoal answer always outputs a number of quote-surrounded strings followed by a comma. Find that comma and split the strings there. If there's no comma, the split happens after the quote-surrounded strings, if any.

%`^"|"("?)
$1

Decode each string by deleting the first quote, one quote of each pair, and the last quote. (Assuming this is an encoded string, of course... otherwise this just deletes arbitrary quotes.)

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

Ruby, 81 bytes

->s{s.match(/^(\d+)\.(.*)/m){|m|l=m[1].to_i;[m[2][0,l],m[2][l..-1]||'']}||['',s]}

Try it online!

I don't golf much in Ruby so this can surely be shortened -- tips appreciated.

Idea is to to provide a meta-prefix <number>.rest of string which will split "rest of string" at number. Strings without the meta-prefix just return ["", <original string>]

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

Retina 0.8.2, 17 bytes

^(.*)(.*)\1\1
$2¶

Try it online!

This parses the string as \$xs_1xxs_2\$ for the longest possible \$x\$ due to the greedy nature of regexes. We may choose \$x = 10^{\max \{|s_1|, |s_2|\}}\$ to ensure the desired parse.

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

Haskell, 67 bytes

p""=["",""]
p(' ':c)="":p c
p('#':b:c)|h:t<-p c=(b:h):t
p c=p$'#':c

Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ This isn't a surjection since A##b c d and similar inputs yield three strings as output. You might also want to provide an explanation on how to produce any desired output. \$\endgroup\$ Jan 16 at 20:36
  • \$\begingroup\$ i can produce any two strings with one string: escape all their blanks with a hash and join them with a blank before feeding the joint. that is surjective. and i "read return two strings" as return two strings or more, implicit take 2. \$\endgroup\$ Jan 16 at 21:43
0
\$\begingroup\$

Zsh, 37 bytes

a=("${(@Q)${(z)1}}")
<<<$a[1]'
'$a[2]

Try it online!

  • (z) splits a string according to shell grammar
  • (Q) removes quotes
  • (@) prevents array joining in quotes
  • " " are needed to preserve empty elements

Finally we print the first two strings with a quoted newline between them.

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