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.
-
\$\begingroup\$ Regarding the last requirement: What about the ceil function? \$\endgroup\$Nobody moving away from SE– Nobody moving away from SE2015年04月15日 14:12:19 +00:00Commented Apr 15, 2015 at 14:12
2 Answers 2
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"))
Note the use of the integer divide which is suitable for both python 2.x and 3.
-
\$\begingroup\$ It might be/certainly is over expressive but i'd be tempted to return
""
onmid==0
to be explicit. \$\endgroup\$Aiden Bell– Aiden Bell2015年04月15日 21:11:05 +00:00Commented Apr 15, 2015 at 21:11 -
\$\begingroup\$ @AidenBell - it certainly could be more efficient for sillily small sillies, but the simple
mid==0
would fail to process 1-letter values, like.... "i"... where mid would be 0, but the caps would still need to happen. Perhaps you meantlen(silly) == 0
? \$\endgroup\$rolfl– rolfl2015年04月15日 21:15:19 +00:00Commented Apr 15, 2015 at 21:15 -
\$\begingroup\$ Yes, of course that would be more correct. How silly of me ;) \$\endgroup\$Aiden Bell– Aiden Bell2015年04月15日 21:27:21 +00:00Commented 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\$Veedrac– Veedrac2015年04月21日 07:36:27 +00:00Commented Apr 21, 2015 at 7:36
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)
-
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\$Aiden Bell– Aiden Bell2015年04月15日 21:09:40 +00:00Commented 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
topivot_index
and perhaps move"".join(string_as_list)
to its own line but otherwise this seems like a straightforward list comprehension to me. \$\endgroup\$Scott Lobdell– Scott Lobdell2015年04月16日 19:40:54 +00:00Commented 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\$Aiden Bell– Aiden Bell2015年04月16日 21:02:32 +00:00Commented 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\$Aiden Bell– Aiden Bell2015年04月16日 21:04:17 +00:00Commented 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\$Aiden Bell– Aiden Bell2015年04月18日 17:55:42 +00:00Commented Apr 18, 2015 at 17:55
Explore related questions
See similar questions with these tags.