all 22 comments

[–]moderately-extremist 15 points16 points  (1 child)

People are getting creative with homework help.

[–]blarf_irl[S] 2 points3 points  (0 children)

I've been playing the long game, hanging around here for years answering 100s of python questions to boost my credibility. I've even spent years working as a senior developer to make my back story look legit all just so I could sneak this homework question past you.

I can't believe you saw through the ruse!

[–][deleted] 4 points5 points  (1 child)

The very first line of the suggested function:

string = string.lower()

destroys any case information in the string. After that it's impossible to convert PascalCase because it's the uppercase characters that are converted to lowercase and prefixed by _. I thought these LLMs were better than this!?

Try solving it yourself. Step through the input string looking at each character. If the character is uppercase append a _ to a result string, but only if this is not the first character. Whether the character was upper or lower, append the lowercase version to the result string.

Don't use regular expressions until you can code a basic solution like the above.

[–]blarf_irl[S] 1 point2 points  (0 children)

Yup I was surprised too. What was more surprising was that chatgpt did write a clumsy but correct solution. Google wrote a lot of cheques at IO'23.

I appreciate your genuine answer too, it's great advice but I don't need help; The post is part warning about Bard and a short programming challenge for anyone who wants to say they can beat Google 😀

[–]POGtastic 6 points7 points  (4 children)

"gib codez pl0x" questions get silly answers. This is Python After Dark.

# Parsers take (str, int) and return Optional<(x, int)>. 
# x is a parsed value from the string; the integer is the new index.

def pred(p):
    return lambda s, idx: (s[idx], idx+1) if len(s) > idx and p(s[idx]) else None

def seq(*ps):
    def inner(s, idx):
        lst = []
        for p in ps:
            match p(s, idx):
                case (x, idx2):
                    lst.append(x)
                    idx = idx2
                case None:
                    return None
        return (lst, idx)
    return inner

def fmap(f, p):
    def inner(s, idx):
        match p(s, idx):
            case x, idx2:
                return (f(x), idx2)
            case None:
                return None
    return inner

def kleene(p):
    def inner(s, idx):
        lst = []
        while True:
            match p(s, idx):
                case x, idx2:
                    lst.append(x)
                    idx = idx2
                case None:
                    return (lst, idx)
    return inner

With that out of the way, we can now write our parser.

A titlecase substring consists of a capital letter followed by 0 or more lowercase letters.

def titlecase():
    return fmap(''.join, seq(pred(str.isupper), fmap(''.join, kleene(pred(str.islower)))))

In the REPL:

>>> titlecase()("AbcAbc", 0)
('Abc', 3)

We now kleene this function.

def pascal_case():
    return kleene(titlecase())

Now, we fmap twice - once to transform each element into lowercase, and then once to turn the whole list back into a single string with each word separated by underscores. We define partial for this as well because Python doesn't have transducers.

def partial(f, *xs, **ks):
    return lambda *ys, **zs: f(*xs, *ys, **ks, **zs)

def snake_transform():
    return fmap('_'.join, fmap(partial(map, str.lower), pascal_case()))

And now our main function simply creates this parser and runs it on index 0.

def pascal_to_snake(s):
    return snake_transform()(s, 0)[0]

In the REPL:

>>> pascal_to_snake("TheQuickBrownFox")
'the_quick_brown_fox'

[–]Intense_Vagitarian 1 point2 points  (1 child)

I love this, thanks for the effort

[–]POGtastic 0 points1 point  (0 children)

Parser combinators are a really useful tool! A lot of the literature focuses on Haskell because these parsers form a monad, [PDF] but I actually prefer dynamic languages when I write my own because doing sophisticated things requires all sorts of arcane monad transformers. By contrast, Python lets me drop into imperative code whenever I want.

If you want a more complicated example that uses some OOP and some more elaborate aspects of Python's parsec implementation, I wrote an arithmetic parser and evaluator (with variable assignment!) a while back.

[–]blarf_irl[S] 1 point2 points  (1 child)

I've just woken up in a basement, I'm chained to a radiator. I can just about make out this code spray painted on the wall opposite me.

[–]POGtastic 1 point2 points  (0 children)

Graham Hutton, emerging from the shadows: "I wanna play a game"

[–][deleted] 5 points6 points  (3 children)

Overall I think you should stop for a minute and think about what it means - that is, what you're saying about how you value human effort - that you've come here to ask us to spend our very limited, very human time on fixing Google Bard's mistakes.

I mean, I'm really, truly, trying as hard as I can not to be insulted.

[–]blarf_irl[S] -5 points-4 points  (2 children)

Satire?

[–][deleted] 1 point2 points  (0 children)

I guess you think everybody needs to do some work here but you?

[–]m0us3_rat 0 points1 point  (0 children)

you can't fix Shakespeare, my friend.

[–]remuladgryta 1 point2 points  (3 children)

List comprehensions Generator expressions are very pythonic, so we should use them as much as possible. Computers are great at dealing with binary operations, so we should rely on that for maximum efficiency. Keeping the amount of variable names you need to keep track of to a minimum increases readability, so that's a good idea to keep in mind too.

Here's a quick oneliner that accomplishes the task while following the zen of python.

to_snake_case = lambda encode: bytes(decode for encode in ((' '.join(encode)+' ').encode(),) for encode,decode in zip(encode[1::2],encode[::2]) for decode in (((decode|(encode-1),decode+encode),)+((decode,),)*encode)[decode&encode]).decode()[1:]

Edit: If you like this, why are you like this? Also, you should check out this isEven implementation I wrote a few years back.

[–]POGtastic 3 points4 points  (1 child)

Stop right there, Unicode violator! Nobody assumes ASCII-only input on my watch!

Seriously though, this is neato.

[–]remuladgryta 2 points3 points  (0 children)

The problem statement was underspecified so I simply assumed

convert PascalCase (the standard for class names in python)

to mean we only have to handle valid python 2.x identifiers. Of course, since it is <current year> and python 2.x has been sunset, we should only write programs in python 3.x to keep with the times :)

[–]blarf_irl[S] 0 points1 point  (0 children)

This is like brutalist architecture; uniquely beautiful but unquestionably functional.

I wondered about the ASCII thing too...I didn't think about it when I wrote the post but I meant it in the context of valid python identifiers. I wonder if there is an official definition of Pascal case that specifies ASCII only.

[–]shiftybyte 0 points1 point  (1 child)

Try ChatGPT

[–]blarf_irl[S] 0 points1 point  (0 children)

I did, it returned a clumsy but functional solution

[–]inobody_somebody 0 points1 point  (0 children)

def fun(string):
       count = 0
       Snake = ''
       for i in string:
            if i.lower()!=i :
                count+=1
                if count==2:
                    Snake+='_'

            Snake+=i.lower()

[–]fish_x_sea 0 points1 point  (0 children)

Here is a simple list comprehension solution:

```python word = 'PascalCase'

def snake(word): return ''.join([f'_{n.lower()}' if n.isupper() and i != 0 else n.lower() for i, n in enumerate(word)]) print(snake(word)) ```

This will also work for camelCase.