need help with some fundamentals of for loops. it looks like im so close to fully grasping grabbing an item out of a list, but not quite. examples below

Osnapitsjoey@lemmy.one to Programming@programming.dev – 31 points –

so ill post a few of my failed examples below along with what I came up with as a fix, and then the actual correct code. I feel like im so close to grasping this, but missing some logic. this is for a hangman game.

one of the failed attempts:

import random
word_list = ["aardvark", "baboon", "camel"]
chosen_word = random.choice(word_list)

#Testing code
print(f'Pssst, the solution is {chosen_word}.')

#Create an empty List called display.
#For each letter in the chosen_word, add a "_" to 'display'.
#So if the chosen_word was "apple", display should be ["_", "_", "_", "_", "_"] with 5 "_" representing each letter to guess.


display = ["_"] * len(chosen_word)


guess = input("Guess a letter: ").lower()

#If the letter at that position matches 'guess' then reveal that letter in the display at that position.
#e.g. If the user guessed "p" and the chosen word was "apple", then display should be ["_", "p", "p", "_", "_"].

for letter in chosen_word:
if guess == letter:
for i in range(len(chosen_word)):
display.insert(i, guess)

print(display)

second:

for letter in chosen_word:
  if guess == letter:
    for i in range(len(chosen_word[letter])):
      display.insert(i, guess)

I ended up just saying screw it and went to this:

display = []
for char in chosen_word:
    if guess == letter:
        display += letter
   else:
    display += "_"

correct way of doing it:

import random
word_list = ["aardvark", "baboon", "camel"]
chosen_word = random.choice(word_list)

print(f'Pssst, the solution is {chosen_word}.')

display = []
word_length = len(chosen_word)
for _ in range(word_length):
  display += "_"
print(display)
  
guess = input("Guess a letter: ").lower()


for position in range(word_length):
  letter = chosen_word[position]
  if letter == guess:
    display[position] = letter

print(display)

so as you can see, i get that I can grab specific parts of a list using indices or slices, but somewhere in my brain my logic is wrong. if you guys have struggled with this before or if you have a good youtube video to help me break it down id be beyond thankful!

20

I think you're misunderstanding the insert method. insert keeps everything that was already in the list, including the item at the given index. it just shifts part of it to the right to make room for the thing you're inserting. Directly changing the list using display[position] = letter instead replaces the item at the index with letter.

Does that make sense?

Yeah I figured that one out from the documentation. I with I saved more of my trial and errors so I could show you guys what I needed help with better. I tried the list.insert(x) and then I would do a list.pop(i+1) as well 😂

I guess what I need help with is I keep messing up where I would put "for x in y: z= y [in position z

I had a few tries with it written

For letter in range(len(chosen_word)) : If letter == guess: Display[I] = letter

But this would grab all the letters and change all blanks to the guess letter

Instead of blindly trying code until it works I would suggest you to write on paper the distinct steps that are required to solve the problem.

Imagine you are the computer and you can do nothing else but what Python allows you. How do you solve the problem ?

Usually people do this exercise on a small example. Then they generalise the approach when they find examples where it does not work.

Ahhh you know what. This would help me. Because when I'm stumped, I'm definitely just "blindly" trying different orders of things and getting frustrated. Thank you very much for the tip

Yeah sometimes you just have to take a step back and think again. Then you will think more clearly and actually know what you wrote :) good luck!

Having the output of each thing you tried would help us get a feel for where your code was messing up without us having to run it ourselves to get the output.

That said, for code snippet 1, you're inserting the letter instead of replacing the underscore with the letter. Not only that, but your for-loop essentially does the following:

  • loop over the length of chosen_word
  • if guess is in the above loop
    • iterate over the display array and add guess that many times (effectively doubling the `display array)

Your second code snippet does the same thing, but with actual formatting so that Python could run the code.

I believe your third code snippet introduces char but then returns to letter. It might work if you replaced char with letter again. Also += letter will add the letter to the end of display, which is not what you want to do.

I did my own version of Hangman in Python a couple years ago if you want to look at the code and see what I did. I'm just a hobbyist, so it's not fantastic, but it might give you an idea of how someone else has approached the problem.

Don’t forget Python’s amazing list comprehension syntax!

guess = input(“Guess a letter:”).lower()
display = [ letter if letter == guess else “_” for letter in word ]

Just one part of your question, but it saves a lot of futzing around with indices and replaces.

Even as an experienced python dev I sometimes prefer explicit for loops over list comprehensions. I think for people who didn't even grasp the concept of a for loop they are more confusing than helping.

Yeah, I'm not a fan of them overall. I think multiple lines is more readable. There are a LOT of people that disagree with me though.

I thought I was the only one... to me unless it's a super-simplistic comprehension, it has a similar effect as when C programmers write if (xx = !(1 == (a ? !c : 34 ^ blit_target))) {. Congratulations, you fit it all on one line! At the expense of totally destroying my train of thought when I'm trying to scan down the code and figure out what the hell's going on. Well done.

I think what tripped you up here is that you iterated over the wrong object. In your second solution:

for letter in chosen_word:
  if guess == letter:
    for i in range(len(chosen_word[letter])):
      display.insert(i, guess)

while in the correct solution:

for position in range(word_length):
  letter = chosen_word[position]
  if letter == guess:
    display[position] = letter

The most important difference here is that in your code block, you iterate over the letters, ie. 'a', 'a', 'r', ..., while in the second you iterate over the numerical indices of the string, 0, 1, 2, .... In this specific use case, it's much easier to use the numerical indices - because you can see how the second code block is using position in two places - once to retrieve the letter from the solution, and then again to update the display when the if condition matches.

Usually we prefer iteration using the method you used in your solution. But in this case, it's easier to just iterate by index because you're retrieving the element from one string, and updating the same position in the other string. You have no way of knowing what position to modify in display unless you have the numerical position, so it's much easier to iterate that way in this case.

This is a pretty compact and - I think - easy to read way of doing it:

while(display != list(chosen_word)):

guess = input("Guess a letter: ").lower()

display = list(map(lambda c, d: c if d != '_' or c == guess else d, chosen_word, display))

print(display)

print("Congrats! You did it!")

Mapping over an array is a pretty powerful tool and also using ternary expressions. If you're not familiar, a map basically just iterates over an array and runs a function on that item, replacing it with whatever the return value of the function is.

For example:

ones = [1, 1] twos = list(map(lambda n: n + 1, ones))

It's running the lambda function with n as a parameter and returning n + 1, and it's pulling the numbers from the array "ones".

Then ternary expressions I also find quite powerful. The format of which is basically:

(result if true) if (condition to check) else (result of false)

Or:

2 if 1 + 1 == 2 else "You broke math. How did you do that?"

I think the other guys have already explained it quite well, but here are some more goodies that might interest you.

Your first attempt of filling the display is the more pythonic way in my opinion and it works, so instead of initializing an empty array and the filling it, just use display = ["_"]*word_length

Also for evaluating if the guess is in the word, there is a very nice iterator called enumerate, that hands you two values, the index and the actual value of the item, so you can use it like this:

for position, letter in enumerate(chosen_word):
    if letter == guess:
        display[position] = letter

Also, to play the full game you want to surround your guessing part with a while loop, so you can keep guessing until you have found the word. For this you will have to create a list of characters that resemble your chosen_word. There are several ways to do so and I will try to explain some of them.

Here we are using the unpacking asterisk, that unpacks each character of your string into an item in the list

while (display != [*chosen_word]):
    guess = input("Guess a letter: ").lower()

    for position, letter in enumerate(chosen_word):
        if letter == guess:
            display[position] = letter
    print(display)

Another way would be explicitly casting the string into a list with the list function like this:

while (display != list(chosen_word)):

Last but not least we could use something like list comprehension, which is seen as very pythonic but a bit weird to look at when you are not used to it.

while (display != [letter for letter in chosen_word]):

What this essentially does is the same as creating a for loop and filling a list like this, but more comprehensive:

chosen_word_list = []
for letter in chosen_word:
    chosen_word_list.append(letter)

W3Schools has some nice info about list comprehension. It is a rather advanced concept though so don't let it bother you if you don't get it right away.

Happy coding :)

Thank you very much! Yeah I'm just having problems with remembering where to put the x[y] in loops. I've done a few free classes and keep getting hung up on that part. It's like my brain is having problems grasping it. I showed an example in another comment

Not sure how to help you with this, since that will change with what you want to archive in the loop. But maybe writing it in pseudo code might help:

For each letter in the word

for letter, position in enumerate(word):

You want to check if your guess is the letter

if letter == guess:

If it is you want to add the letter to your display , exactly on the position it is in the original word

display[position] = letter

Does that kind of thinking help?

So - ChatGPT is great for breaking down concepts like this and answering questions about the basics until you get the hang of it. I would recommend crafting some programs while asking it for help on anything you get stuck with, until you can craft the programs without involving it (and still you can paste the programs into it and ask for pointers / fixes that it can see). I'm currently learning Go with assistance from ChatGPT and it's hugely useful.

One other angle you could come at it from -- this might venture into unpopularopinion territory, but I would actually recommend learning C at a very early stage. It'll be tedious to make nontrivial programs with it, so you may not want to stick with it for your real projects, but since everything is bytes and pointers it gives you a chance to get extremely solid with the fundamentals before you start mucking around with slices / hashes / etc. I would recommend to try to get this particular problem working using C, which will be more work, but then when you come back and learn the Python concepts I think they'll make a lot of sense and you'll feel capable with them. IDK if it'll work that way for you, but that's what I did and I feel like it was a good way to go.

Best of luck + keep plugging

Yeah I've actually been using chatgpt as well as a few other resources! My biggest gripe is that chatgpt can't really teach without showing. I want to understand where my logic was flawed, and be guided towards the correct answer, instead chatgpt will do a good job at explaining what I did wrong, and then showing me the correct code.

So c is a good starter language? Cuz I'm at the point now that I can just stackoverflow my way into making a smaller project, but I really want to learn how this all works and learn the fundamentals so I'm fluent

In my opinion, C is a difficult starter language, but one that sets you up for very good abilities in the future, because a lot of other languages' core concepts are built on C concepts. It's all bytes, and if you're comfortable with bytes then that'll put you ahead of someone who only knows the general concepts. It's similar to working construction before you go into architecture -- your buildings will be better even if you're thinking at a higher level than nails and shingles when you do most of your design.

So, ChatGPT is tuned to sort of do things for you, but you can still have it do the troubleshooting with the right prompting instead of just rewriting:

GPT-4 Prompt

I'm trying to understand what's going wrong with my code -- can you point me to the line that's causing the issue and quickly explain what might be wrong?

import random
word_list = ["aardvark", "baboon", "camel"]
chosen_word = random.choice(word_list)

#Testing code
print(f'Pssst, the solution is {chosen_word}.')

#Create an empty List called display.
#For each letter in the chosen_word, add a "_" to 'display'.
#So if the chosen_word was "apple", display should be ["_", "_", "_", "_", "_"] with 5 "_" representing each letter to guess.


display = ["_"] * len(chosen_word)


guess = input("Guess a letter: ").lower()

#If the letter at that position matches 'guess' then reveal that letter in the display at that position.
#e.g. If the user guessed "p" and the chosen word was "apple", then display should be ["_", "p", "p", "_", "_"].

for letter in chosen_word:
    if guess == letter:
        for i in range(len(chosen_word)):
            display.insert(i, guess)

print(display)

GPT-4's response

The problem with your code is in this segment:

for letter in chosen_word:
    if guess == letter:
        for i in range(len(chosen_word)):
            display.insert(i, guess)

The logic you're using here is:

  • For each letter in chosen_word,
  • If guess matches the letter,
  • Insert the guessed letter at every index of the chosen_word.

This will make the display list longer than it should be, and the letters will not replace the underscores but instead will be added alongside them.

Instead, you should check if the guessed letter matches a letter in the chosen word at a specific position, and if it does, replace the underscore at that position in the display list.

Here's the corrected segment of code:

for i in range(len(chosen_word)):
    if chosen_word[i] == guess:
        display[i] = guess

With this, the guessed letter will replace the corresponding underscore in the display list.

Based on the first example:

If you want to help yourself a bit, enumerate your for loop. enumerate turns an iterable (like a list, or in this case a string) into an iterable of tuples, with contents being an int representing the index of an item and the item itself:

for (i, letter) in enumerate(chosen_word):

(Side note, the parenthesis surrounding (i, letter) are optional. I purposely included them to show that it's a tuple.)

i will be the index of each character, and letter will be the character itself. You can then do:

if letter == guess:

And to wrap it up, do list assignment by index. Someone already mentioned why not to use insert in this scenario, so I won't repeat them. The following will instead overwrite the item at display index i with the guessed character:

display[i] = guess