11
\$\begingroup\$

I found the following challenge online:

Create a function that takes a string and returns that string with the first half lowercased and the last half uppercased.

eg: foobar == fooBAR

If it is an odd number then 'round' it up to find which letters to uppercase.

That sounded easy enough but I found the last requirement a little tricky. Eventually this is what I came up with:

def sillycase(silly):
 length=len(silly)
 firstHalf = silly[:length / 2 + (length % 2)].lower()
 secondHalf = silly[length / 2 + (length % 2):].upper()
 return firstHalf + secondHalf 

but that seems repetitive and inefficient.

What could I have done to make it slicker, without sacrificing readability? I was actually a little surprised python didn't have a built in method to split a string in half.

asked Apr 15, 2015 at 3:48
\$\endgroup\$
1
  • \$\begingroup\$ Regarding the last requirement: What about the ceil function? \$\endgroup\$ Commented Apr 15, 2015 at 14:12

2 Answers 2

15
\$\begingroup\$

Midpoint math is often a pain in the whatnot, but a real trick is to add 1 to the length before halving it. Additionally, instead of repeating the math multiple times, you can do it just once.

def sillycase(silly):
 mid=(len(silly) + 1) / 2
 firstHalf = silly[:mid].lower()
 secondHalf = silly[mid:].upper()
 return firstHalf + secondHalf 

At that point, it becomes small enough to not need the temp variables:

def sillycase(silly):
 mid = (len(silly) + 1) // 2
 return silly[:mid].lower() + silly[mid:].upper() 
print (sillycase("hello"))
print (sillycase("helloa"))
print (sillycase("helloab"))

http://ideone.com/CqUdl5

Note the use of the integer divide which is suitable for both python 2.x and 3.

answered Apr 15, 2015 at 4:06
\$\endgroup\$
4
  • \$\begingroup\$ It might be/certainly is over expressive but i'd be tempted to return "" on mid==0 to be explicit. \$\endgroup\$ Commented Apr 15, 2015 at 21:11
  • \$\begingroup\$ @AidenBell - it certainly could be more efficient for sillily small sillies, but the simple mid==0would fail to process 1-letter values, like.... "i"... where mid would be 0, but the caps would still need to happen. Perhaps you meant len(silly) == 0? \$\endgroup\$ Commented Apr 15, 2015 at 21:15
  • \$\begingroup\$ Yes, of course that would be more correct. How silly of me ;) \$\endgroup\$ Commented Apr 15, 2015 at 21:27
  • 1
    \$\begingroup\$ I'm just mentioning it for completeness, but personally I find - (-x // y) should be the canonical code for "round-up" division: it's lossless, works on Python 3, doesn't involve constants, always works for negative divisors and even works for floating point. \$\endgroup\$ Commented Apr 21, 2015 at 7:36
1
\$\begingroup\$

If it were me I'd just go for brevity and ensure the function name is expressive:

def silly_case(input_string):
 return "".join([char.lower() if index < (len(input_string) + 1)/ 2 else char.upper() for index, char in enumerate(input_string)])

EDIT:

I've gotten lots of criticism for my answer for stuffing things to one line, but I'm still a fan of list comprehensions, and I find it completely intuitive. Based on the feedback, here's how I would implement this function:

def silly_case(input_string):
 pivot_index = (len(input_string) + 1) / 2
 char_list = [char.lower() if index < pivot_index else char.upper() for index, char in enumerate(input_string)]
 return "".join(char_list)
answered Apr 15, 2015 at 4:12
\$\endgroup\$
10
  • 2
    \$\begingroup\$ Brevity as a positive falls down when code isn't readable. Unreadable or unreasonable code costs more in terms of bugs than a few extra tokens. Perhaps i'm missing the point of the site, but if I saw this going in to production the author would be forced to rewrite it. \$\endgroup\$ Commented Apr 15, 2015 at 21:09
  • \$\begingroup\$ If a function is 1 line and it's well-named (here it's just "silly_case"), and there's test coverage for that function, do you still find that 1 line unreadable? If anything I might change (len(input_string) + 1)/ 2 to pivot_index and perhaps move "".join(string_as_list) to its own line but otherwise this seems like a straightforward list comprehension to me. \$\endgroup\$ Commented Apr 16, 2015 at 19:40
  • \$\begingroup\$ "If a function is 1 line and it's well-named (here it's just "silly_case"), and there's test coverage for that function, do you still find that 1 line unreadable?" ... yes, a million times yes. Character efficiency in code is not equal to computational efficiency, and even if it were, i'd still insist on a rewrite unless it was in some uber critical performance path. Yes, the situations in which this is acceptable IMHO are very, very rare. 99.99% of the time i'd take readable code over fast or succinct code. \$\endgroup\$ Commented Apr 16, 2015 at 21:02
  • 1
    \$\begingroup\$ In short, I see zero benefit to this being a long one liner vs a readable multiple line implementation ... that's ignoring the fact it is 129 characters long! \$\endgroup\$ Commented Apr 16, 2015 at 21:04
  • 1
    \$\begingroup\$ @ScottLobdell - In fairness, this is a code review site. I love list comprehension, but not at that price ;) Anyway, each to their own. \$\endgroup\$ Commented Apr 18, 2015 at 17:55

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.