45
\$\begingroup\$

For today's challenge, you must write a program or function that alternates the case of a string. However, you must ignore non-alphabetic characters. This means that every alphabetic character must have a different case than the preceding and following alphabetic character. This is slightly more complex than uppercasing every other letter for example. If you take a string such as

hello world

and convert every other character to uppercase, you'll get:

hElLo wOrLd

As you can see, the lowercase o is followed by a lowercase w. This is invalid. Instead, you must ignore the space, giving us this result:

hElLo WoRlD

All non-alphabetic characters must be left the same. The output can start with upper or lowercase, as long as it consistently alternates. This means the following would also be an acceptable output:

HeLlO wOrLd

Your program should work regardless of the case of the input.

The input string will only ever contain printable ASCII, so you don't have to worry about unprintable characters, newlines or unicode. Your submission can be either a full program or a function, and you may take the input and output in any reasonable format. For example, function arguments/return value, STDIN/STDOUT, reading/writing a file, etc.

Examples:

ASCII                                   ->  AsCiI
42                                      ->  42
#include <iostream>                     ->  #InClUdE <iOsTrEaM>
LEAVE_my_symbols#!#&^%_ALONE!!!         ->  lEaVe_My_SyMbOlS#!#&^%_aLoNe!!!
PPCG Rocks!!! For realz.                ->  PpCg RoCkS!!! fOr ReAlZ.
This example will start with lowercase  ->  tHiS eXaMpLe WiLl StArT wItH lOwErCaSe
This example will start with uppercase  ->  ThIs ExAmPlE wIlL sTaRt WiTh UpPeRcAsE
A1B2                                    ->  A1b2

Since this is , standard loopholes apply and the shortest answer in bytes wins!

\$\endgroup\$
  • 35
    \$\begingroup\$ Ugh, I've only just realised this was that meme xD \$\endgroup\$ – Beta Decay May 25 '17 at 17:26
  • \$\begingroup\$ @BetaDecay Hahaha, that was not my intention. More just unfortunate timing. I though of it as a chat-mini-challenge, and I like the idea behind it because it's subtly harder than it seems. \$\endgroup\$ – James May 25 '17 at 17:28
  • 4
    \$\begingroup\$ The next challenge is to print an ascii spongebob à la cowsay \$\endgroup\$ – Frambot May 25 '17 at 19:41
  • 1
    \$\begingroup\$ Darn it! I just wrote a CJam script for this (as in yesterday) and deleted it. \$\endgroup\$ – Esolanging Fruit May 26 '17 at 1:58
  • 2
    \$\begingroup\$ There is some missed potential for the title or at least the examples using either penguin of doom or sporks. \$\endgroup\$ – Ian May 26 '17 at 6:00

36 Answers 36

1
2
0
\$\begingroup\$

Python 3, 95 bytes

Not nearly as golfed as @Rod's, but I'm posting it nevertheless.

lambda s:"".join([s[i],[s[i].lower(),s[i].upper()][i%2]][s[i].isalpha()]for i in range(len(s)))

Try it online!

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

J, 40 bytes

(2|+/\@:e.&Alpha_j_)`(tolower,:toupper)}

Try it online!

This follows almost directly from the definition of Composite Item (}):

We stack the upper and lowercase version of the input for our two possibilities using (tolower,:toupper).

Next we create a boolean list to indicate whether each character is alphabetic: e.&Alpha_j_ and take the scan sum of that: +/\@: which creates a monotonically increasing list which increases only on alphabetic characters. Finally we turn that into a boolean list where evens are 0 and odds are 1: 2|.

Putting those parts together means we alternate between the lower and uppercase versions of our input whenever we encounter a new alphabetic character, and only then.

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

C# (Visual C# Interactive Compiler), 84 bytes

int m,c;for(;(c=Read())>0;)Write((char)(c>64&c<91|c>96&c<123?m++%2>0?c|32:c&~32:c));

Try it online!

Modified version of The Lethal Coder's answer that takes advantage of some of the features of the interactive compiler.

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Japt v2.0a0, 11 bytes

r\lÈc^H*°Tv

Try it

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Acc!!, 110 bytes

N*8
Count i while _/256 {
_+_/520*(727/_)*2+_/776*(983/_)*4
Write _/8+32*(_%4/3-_%8%5/4)
(_+(_%8+2)/4)%2+N*8
}

Try it online!

Algorithm

Set toLower flag to 0
Read a character
While not EOL:
  Calculate isUpper and isLower flags
  If isUpper && toLower, output (character + 32)
  If isLower && !toLower, output (character - 32)
  Else, output character unchanged
  If isUpper or isLower, toggle toLower
  Reset isUpper and isLower, and read a new character

Accumulator partitioning

We reserve the lowest three bits of the accumulator for storing the flags: toLower in the 1's place, isUpper in the 2's place, and isLower in the 4's place. The rest of the accumulator, at the 8's place and above, stores the input character.

For example, with input of Hello World, after reading the H and calculating flags, the accumulator has a value of 578, or in binary:

1001000 0 1 0
   |    | | \-> toLower (false)
   |    | \---> isUpper (true)
   |    \-----> isLower (false)
   \----------> ASCII code for 'H'

After reading the space, the accumulator is 257:

0100000 0 0 1
   |    | | \-> toLower (true)
   |    | \---> isUpper (false)
   |    \-----> isLower (false)
   \----------> ASCII code for ' '

Code

Line 1

N*8

Stores the first input character and sets all the flags to 0.

Line 2

Count i while _/256 {

Loops while the accumulator value is >= 256 (i.e. the character's ASCII code is at least 32).

Line 3

_+_/520*(727/_)*2+_/776*(983/_)*4

Calculates the isUpper and isLower flags. The main trick here is using integer division for the inequalities. For example, if x is a printable-ASCII code, then 65 <= x is equivalent to x/65 with integer division: values smaller than 65 become 0, values between 65 and 126 become 1, and we don't have to worry about anything larger than 126. We can use this approach for all four inequalities (keeping in mind that the ASCII codes are all multiplied by 8 in the accumulator).

isUpper:

65 <= char <= 90
65*8 <= acc <= 90*8+7
acc/520 == 1 && 727/acc == 1

Expression: _/520*(727/_)

isLower:

97 <= char <= 122
97*8 <= acc <= 122*8+7
acc/776 == 1 && 983/acc == 1

Expression: _/776*(983/_)

This line, then, takes the accumulator and adds isUpper*2 + isLower*4 to it.

Line 4

Write _/8+32*(_%4/3-_%8%5/4)

Outputs the character, swapping case if necessary.

There are two instances in which we need to modify the ASCII value before output:

  1. isUpper is 1, toLower is 1
    • We need to convert the uppercase letter to lowercase by adding 32
    • The three flag bits are 011, i.e. acc%4 == 3
  2. isLower is 1, toLower is 0
    • We need to convert the lowercase letter to uppercase by subtracting 32
    • The three flag bits are 100, i.e. acc%8 == 4

We can simulate the equality test using mod and int-div. For example, acc%8 == 4 is equivalent to acc%8%5/4:

acc%8      0 1 2 3 4 5 6 7
     %5    0 1 2 3 4 0 1 2
       /4  0 0 0 0 1 0 0 0

This line, therefore, takes 1 if we need to convert upper-to-lower, subtracts 1 if we need to convert lower-to-upper, multiplies that by 32, adds it to the ASCII code, and outputs it.

Line 5

(_+(_%8+2)/4)%2+N*8

Toggle the toLower flag if necessary; inputs and stores a new character.

We need to toggle toLower if the character was a letter, i.e. either isUpper or isLower was set. The possible flag bit values are 010, 011, 100, and 101--i.e. 2 through 5 (note that 6 and 7 are not possible because a letter can't be both upper- and lowercase). This expression gives 1 for those values and 0 otherwise:

 _%8       0 1 2 3 4 5
    +2     2 3 4 5 6 7
(     )/4  0 0 1 1 1 1

Call this value isLetter. Then we want the new value of toLower to be toLower XOR isLetter (if isLetter is true, swap the value of toLower; otherwise, leave it unchanged). We can do XOR by adding the two values mod 2. Finally, we also read another character with N, multiply it by 8, and add it in.

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Javascript, 104 bytes

I'm kind of new to this but wanted to submit anyway.

x=>x.split('').forEach((s,i)=>x+=i%2?s.toUpperCase():s.toLowerCase())||x.substring(x.length/2,x.length);

It takes in a string, splits it into an array so it can loop through with forEach and fit in as a one-liner, then goes through each character and index and adds the uppercase or lowercase version of the character depending on if the index is odd or not, then "or"s it with what I want to return (since otherwise it's undefined) and returns the second half of the string, so I don't have to clear the string to begin with or declare a new one.

However, this doesn't work with non-letter characters, so here's my pseudocode for something that would do that:

const sarcasticText = str=> {
    let newStr = '', numSkip = 0;
    for(let i=0; i<str.length; i++) {
        numSkip += str[i].match(/[a-z]/i) ? 0 : 1;
        newStr += (i + numSkip) % 2 ? str[i].toUpperCase() : str[i].toLowerCase();
    }
    return newStr;
};

Basically, I'd implement a counter to check how many characters to offset.

| improve this answer | |
\$\endgroup\$
1
2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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