Help please

Recently I asked here about where "end" goes, in relation to code
blocks. I was given some good answers which helped but now either I'm
having a same / similar problem or this is perhaps something
completely different.

What I'm attempting to do in the code below is
1-Search a directory for files
2-Check if the files exist
3-Ask user to confirm if they want file to be moved
4-If user answers no , then file is removed from array.
5-After user has gone through the entire list one by one, then the
updated array should list out the files (minus the ones that said no
to move on.

Right now, the problem is step five (listing the updated array) is
happening with each question. Plus it looks like the elements are not
being removed where appropriate. Please let me know if I ask too many
questions here :).

Dir.chdir 'C:/PicturesMoved'

# First we find all of the pictures to be moved.
pic_names = Dir['C:/PicturesMoved/*.{BMP,bmp}']

pic_names.each do |name|
  fcheck = FileTest.exist?(name)
     if fcheck == true
       puts 'Do you wish to move ' + name + 'file?'
       puts 'Please answer "yes" or "no" '
       else puts 'file does not exist'

       end
     end

       response = true
        while (response)
          decision = gets.chomp.downcase
          if decision == 'yes'
            response = false
            else
              puts 'File will not be moved'
              pic_names.delete(name)
            end
          end

          puts 'these files will be moved'
          pic_names.each do |name|
            puts name

          end

p.s. The code is indented in my email here , hopefully it remains
formatted as such when it hits the list.

After I sent this I regretted it. I should try to work this out on my own.

Stuart

···

On 7/1/06, Dark Ambient <sambient@gmail.com> wrote:

Recently I asked here about where "end" goes, in relation to code
blocks. I was given some good answers which helped but now either I'm
having a same / similar problem or this is perhaps something
completely different.

What I'm attempting to do in the code below is
1-Search a directory for files
2-Check if the files exist
3-Ask user to confirm if they want file to be moved
4-If user answers no , then file is removed from array.
5-After user has gone through the entire list one by one, then the
updated array should list out the files (minus the ones that said no
to move on.

Right now, the problem is step five (listing the updated array) is
happening with each question. Plus it looks like the elements are not
being removed where appropriate. Please let me know if I ask too many
questions here :).

Dir.chdir 'C:/PicturesMoved'

# First we find all of the pictures to be moved.
pic_names = Dir['C:/PicturesMoved/*.{BMP,bmp}']

pic_names.each do |name|
  fcheck = FileTest.exist?(name)
     if fcheck == true
       puts 'Do you wish to move ' + name + 'file?'
       puts 'Please answer "yes" or "no" '
       else puts 'file does not exist'

       end
     end

       response = true
        while (response)
          decision = gets.chomp.downcase
          if decision == 'yes'
            response = false
            else
              puts 'File will not be moved'
              pic_names.delete(name)
            end
          end

          puts 'these files will be moved'
          pic_names.each do |name|
            puts name

          end

p.s. The code is indented in my email here , hopefully it remains
formatted as such when it hits the list.

It looks a bit to me like the formatting didn't come through entirely intact, but this bit still gives me a hunch at where you're going wrong, and it's still a question of where the 'end' goes for a statement.

'if' statements behave a little differently than loops: the 'else' and 'elsif' (if they're used) don't require their own 'end's, they're considered part of the if statement, and are generally indented to the same level as the initial 'if'. In other words: the 'if' is the beginning of the statement, so that's what matches the 'end'. Here's the usual formatting for the 'if' statement:

if <condition>
   <code>
elsif <condition>
   <code>
else
   <code>
end

You can have as many elsif statements as you like (including zero), and zero or one else statements, and the whole construct is finished off with an 'end'

If we reformat the snippet of code above, matching the 'end's with what they actually match, it ends up looking like this:

pic_names.each do |name|
   fcheck = FileTest.exist?(name)
   if fcheck == true
     puts 'Do you wish to move ' + name + 'file?'
     puts 'Please answer "yes" or "no" '
   else
     puts 'file does not exist'
   end
end

From that I think you can see what part of the problem is. You've got what looks like a similar construction in the while loop as well. I'm not sure that's the only problem in the code, but fixing up your 'end's will certainly get you on your way.

matthew smillie.

PS: Is your editor using tabs or spaces for indentation? I tried to send myself code over gmail and it came through just fine unless I had a mix of spaces and tabs as indentation, which turned it into muck. If this is the case, there may be an option to indent using spaces. Otherwise, you could probably enter the code manually using spaces to preserve indentation.

···

On Jul 1, 2006, at 13:24, Dark Ambient wrote:

Recently I asked here about where "end" goes, in relation to code
blocks. I was given some good answers which helped but now either I'm
having a same / similar problem or this is perhaps something
completely different.

pic_names.each do |name|
fcheck = FileTest.exist?(name)
    if fcheck == true
      puts 'Do you wish to move ' + name + 'file?'
      puts 'Please answer "yes" or "no" '
      else puts 'file does not exist'

      end
    end

Dark Ambient wrote:

Please let me know if I ask too many
questions here :).

You can never ask too many questions here.

There's the chance, of course, that they may not always be answered, or answered in a timely manner, but so long as you show good faith in trying to work things out on your own, and first check the available resources (Google, RubyGarden wiki, ruby-doc.org, ri, ruby-talk list archives, and so on) you should be fine.

···

--
James Britt

http://www.ruby-doc.org - Ruby Help & Documentation
Ruby Code & Style - The Journal By & For Rubyists
http://www.rubystuff.com - The Ruby Store for Ruby Stuff
http://yourelevatorpitch.com - Finding Business Focus

if <condition>
   <code>
elsif <condition>

> <code>
> else

   <code>
end

You can have as many elsif statements as you like (including zero),
and zero or one else statements, and the whole construct is finished
off with an 'end'

So, all if / else must have a following end ? I realize it must be
somewhere, but do they always necessarily follow the if / else or
perhaps could they be further down depending on the code logic ?

PS: Is your editor using tabs or spaces for indentation? I tried to
send myself code over gmail and it came through just fine unless I
had a mix of spaces and tabs as indentation, which turned it into
muck. If this is the case, there may be an option to indent using
spaces. Otherwise, you could probably enter the code manually using
spaces to preserve indentation.

I think guilty as charged. Using a combo of tabs and spaces in Scite.
Here (and I dont' want to keep posting new designs) but here is
something that looks logical to me: What is happening with this code
is it keeps asking about the same file. I thought the 'each do' would
continue to loop through. Doesn't seem to be the case. I tried
adding something to the while like 'until pic_names.length' , didn't
go over well. :slight_smile:
btw , i removed tabs and spaces from paste, and then spaced in Gmail.

pic_names = Dir['C:/PicturesMoved/*.{BMP,bmp}']

      pic_names.each do |name| #start of array loop
      continue = true # setting up the conditon to end the while loop
                 while (continue)
                 fcheck = FileTest.exist?(name) #should be confirming
files exist in directory

#probably this is out of context though I don't

#believe it's doing any damage
                       if fcheck == false
                        puts 'file does not exist'

                       else
                        puts 'Do you wish to move ' + name +
'file?,"yes" or "no" ?'
                           decision = gets.chomp.downcase

                       end # for 1st if block perhaps wrong ?

                       if decision == 'no'
                          puts 'file will not be moved'
                          pic_names.delete(name)

                          end # for 2nd if block
                      end # for array loop
                  end # for while loop

···

On 7/1/06, Matthew Smillie <M.B.Smillie@sms.ed.ac.uk> wrote:

On Jul 1, 2006, at 13:24, Dark Ambient wrote:

i thought the limit was 8?

:wink:

-a

···

On Sun, 2 Jul 2006, James Britt wrote:

Dark Ambient wrote:

Please let me know if I ask too many
questions here :).

You can never ask too many questions here.

--
suffering increases your inner strength. also, the wishing for suffering
makes the suffering disappear.
- h.h. the 14th dali lama

So, all if / else must have a following end ? I realize it must be
somewhere, but do they always necessarily follow the if / else or
perhaps could they be further down depending on the code logic ?

I'm not 100% sure exactly what you mean here. You can have as much code as you like between the 'if' or 'else' and the corresponding 'end'. What you can't do is overlap the start of one statement with the end of another, which seems to be what you're saying here:

pic_names.each do |name| #start of array loop
  while (continue)
    [...]
  end # for array loop
end # for while loop

Judging from your code, you're still mixed up about which 'end' matches which statement - you've labelled the end of the while loop and the end of the array loop backwards from what they actually are.

You need to remember that the statements are nested one inside the other. Suppose you have something like the following:

   a.each do |x| # a <--------
                     # |
     b.each do |y| # b <----- |
                     # | |
       c.each do |z| # c <-- | |
         [...] # | | |
       end # c --- | |
                     # | |
     end # b ------ |
                     # |
   end # a ---------

Three starts to the statements, and three ends. The indentation gives you a clue as to which 'end' belongs to which loop. In other words, if you *start* a set of statements a, b, c, then you have to finish them in *reverse* order: c, b, a.

if/elsif/else is only very slightly different. The 'elsif' and/or 'else' aren't separate statements, they're part of the main 'if' statement. For example, you can't have 'elsif foo' in your code outside of an 'if' statement. So all together, they only need one 'end'. Modifying the above diagram:

   a.each do |x| # a <--------
                     # |
     b.each do |y| # b <----- |
                     # | |
       if c # c <-- | |
         [1] # | | |
       else # | | |
         [2] # | | |
       end # c --- | |
                     # | |
     end # b ------ |
                     # |
   end # a ---------

The code in [1] gets execute when something == true, otherwise the code in [2] gets executed. You can think of the 'else' as implicitly ending the 'if', I suppose, but you still only need one 'end'. [1] or [2] could contain other if statements, and they'd nest just like anything else would:

   if a # ------
     if b # ---- |
       [a true, b true] # | |
     else # | |
       [a true, b false] # | |
     end # ---- |
   else # |
     if b # ---- |
       [a false, b true] # | |
     else # | |
       [a false, b false] # | |
     end # ---- |
   end # ------

So, if you look at your code, you'll see a few things:
1.a) 'continue' is never set to false, so the while-loop never ends, which implies:

1.b) the end of the each-loop is never reached, and so 'name' is never set to the next element of the array.

2) the 'end' of the first if block does 'end' the first if block, certainly, but I don't think that's where you want it to be. Think about what would happen if the file didn't exist - decision wouldn't have a correct value, and the second if statement would do something unpredictable (probably raise an exception in this specific case).

matthew smillie.

···

On Jul 1, 2006, at 15:39, Dark Ambient wrote:

That's one; only seven more, Ara.

:slight_smile:

···

ara.t.howard@noaa.gov wrote:

On Sun, 2 Jul 2006, James Britt wrote:

Dark Ambient wrote:

Please let me know if I ask too many
questions here :).

You can never ask too many questions here.

i thought the limit was 8?

--
James Britt

2 of the 8 must be YAML related. Credits are needed for collections
or non standard libraries.

Stuart

···

On 7/1/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:

On Sun, 2 Jul 2006, James Britt wrote:

> Dark Ambient wrote:
>> Please let me know if I ask too many
>> questions here :).
>
> You can never ask too many questions here.

i thought the limit was 8?

:wink:

-a
--
suffering increases your inner strength. also, the wishing for suffering
makes the suffering disappear.
- h.h. the 14th dali lama

[1]or [2] could contain other if statements, and they'd nest just like
anything else would:

   if a # ------ <-----------|
    if b # ---- | <------------|--------------------------|
       [a true, b true] # | | | |
     else # | | | |
       [a true, b false] # | | | |
    end # ---- | <----- | |
   else # | |
     if b # ---- | <--------------------------------------|----------
       [a false, b true] # | | | |
     else # | | | |
       [a false, b false] # | | | |
     end # ---- | <------------------------------------ | |
   end # ------ This ends first 'if b' above <--------

Did I make the right connections ? If I did it's still confusing
(sure I'll get used to it) to have the else right after the end.

matthew smillie.

Stuart

···

On 7/1/06, Matthew Smillie <M.B.Smillie@sms.ed.ac.uk> wrote:

From what I can see, no. The first statement started is the *last* one ended, not the first one.

if a # start of 1
   if b # start of 2

   else # else clause of 2

   end # end of 2
else # else clause of 1
   if c # start of 3

   else # else clause of 3

   end # end of 3
end # end of 1

Just out of curiosity, what did you think the connections I drew were meant to represent?

matthew smillie.

···

On Jul 1, 2006, at 23:36, Dark Ambient wrote:

On 7/1/06, Matthew Smillie <M.B.Smillie@sms.ed.ac.uk> wrote:

[1]or [2] could contain other if statements, and they'd nest just like
anything else would:

   if a # ------ <-----------|
    if b # ---- | <------------|--------------------------|
       [a true, b true] # | | > >
     else # | | > >
       [a true, b false] # | | > >
    end # ---- | <----- > >
   else # > >
     if b # ---- | <--------------------------------------|----------
       [a false, b true] # | > > >
     else # | > > >
       [a false, b false] # | > > >
     end # ---- | <------------------------------------ > >
   end # ------ This ends first 'if b' above <--------

Did I make the right connections ? If I did it's still confusing
(sure I'll get used to it) to have the else right after the end.

Just out of curiosity, what did you think the connections I drew were
meant to represent?

I blanked on the | and ||.

From what I can see, no. The first statement started is the *last*

> one ended, not the first one.

if a # start of 1
   if b # start of 2

   else # else clause of 2

   end # end of 2
else # else clause of 1
   if c # start of 3

   else # else clause of 3

   end # end of 3
end # end of 1

It makes sense to me, I see it, I'm filing this email as a keeper
because I know I'll need to go back and refer to it. At this point the
best way to proceed for me is to worry less about all the code that
needs to be inside a block and create the scaffolds for the blocks
first with some puts statements just to makes sure things are
branching correctly. I started with the original code I posted in
this thread. I dropped the 'while' loop completely thinking it didn't
make sense considering the array loop was already in place performing
the same behaviour.

Dir.chdir 'C:/PicturesMoved'
pic_names = Dir['C:/PicturesMoved/*.{BMP,bmp}']

     pic_names.each do |name| # (1) array loop
          puts 'Do you wish to move ' + name + 'file?,"yes" or "no" ?'
          decision = gets.chomp.downcase

               if decision == 'no' # 1st if statement
                   puts 'This file will not be moved'
               else
                   puts 'This file will be moved'

               end # end for 1st if statement
     end # end of array loop

This seems to work fine, however if I add this to the 'if statement'
                           if decision == 'no'
                           pic_names.delete(name)
                           puts 'This file will not...........
                              else .........
                           end......

Then the next file in the array is skipped. As an example, if it's
'pic1' and I say 'no', then the next name to come up for the gets
statement is 'pic 3'. I never get to decide on 'pic 2' (saying yes to
moving 'pic 1' moves the next gets to 'pic 2')

So I decided to see what elements is assigned to
'pic_names.delete(name). It's correct (i.e. no to pic 1, puts 'This'
+ name + ' will not be moved' , returns
This C:/PicturesMoved/copypics01 .bmp will not be moved. Yet right
after that prints out I get:
Do you wish to move C:/PicturesMoved/copypics03 .bmpfile?,"yes" or "no" ?
Not sure what happened to pic2 ?

I sense that I may still (as simple as this looks) some errors
somewhere in if / else.

Stuart

···

On 7/1/06, Matthew Smillie <M.B.Smillie@sms.ed.ac.uk> wrote:

I'm responding on my post above (re: why pic_names.delete(name), jumps
an element in the loop.
There must be a reason for this and still would like to some
elucidation on the matter.

However I found by creating a new array pix2bmoved =
and doing a pix2bmoved.push[name], things seem to work right.

Stuart

···

On 7/2/06, Dark Ambient <sambient@gmail.com> wrote:

> Just out of curiosity, what did you think the connections I drew were
> meant to represent?

I blanked on the | and ||.

On 7/1/06, Matthew Smillie <M.B.Smillie@sms.ed.ac.uk> wrote:

> From what I can see, no. The first statement started is the *last*
> one ended, not the first one.
>
> if a # start of 1
> if b # start of 2
>
> else # else clause of 2
>
> end # end of 2
> else # else clause of 1
> if c # start of 3
>
> else # else clause of 3
>
> end # end of 3
> end # end of 1
>

It makes sense to me, I see it, I'm filing this email as a keeper
because I know I'll need to go back and refer to it. At this point the
best way to proceed for me is to worry less about all the code that
needs to be inside a block and create the scaffolds for the blocks
first with some puts statements just to makes sure things are
branching correctly. I started with the original code I posted in
this thread. I dropped the 'while' loop completely thinking it didn't
make sense considering the array loop was already in place performing
the same behaviour.

Dir.chdir 'C:/PicturesMoved'
pic_names = Dir['C:/PicturesMoved/*.{BMP,bmp}']

     pic_names.each do |name| # (1) array loop
          puts 'Do you wish to move ' + name + 'file?,"yes" or "no" ?'
          decision = gets.chomp.downcase

               if decision == 'no' # 1st if statement
                   puts 'This file will not be moved'
               else
                   puts 'This file will be moved'

               end # end for 1st if statement
     end # end of array loop

This seems to work fine, however if I add this to the 'if statement'
                           if decision == 'no'
                           pic_names.delete(name)
                           puts 'This file will not...........
                              else .........
                           end......

Then the next file in the array is skipped. As an example, if it's
'pic1' and I say 'no', then the next name to come up for the gets
statement is 'pic 3'. I never get to decide on 'pic 2' (saying yes to
moving 'pic 1' moves the next gets to 'pic 2')

So I decided to see what elements is assigned to
'pic_names.delete(name). It's correct (i.e. no to pic 1, puts 'This'
+ name + ' will not be moved' , returns
This C:/PicturesMoved/copypics01 .bmp will not be moved. Yet right
after that prints out I get:
Do you wish to move C:/PicturesMoved/copypics03 .bmpfile?,"yes" or "no" ?
Not sure what happened to pic2 ?

I sense that I may still (as simple as this looks) some errors
somewhere in if / else.

Stuart

You're one step away from answering your own question: you're modifying the same array that you're iterating over, which can make things go slightly crazy. Try this example, for instance:

a = (1..10).to_a
a.each { |x|
   puts x
   a.delete(x)
}

This only prints 1, 3, 5, 7, 9. The technical reason for this is that (if I recall correctly) Ruby is using an internal array index to keep track of which element of a it's on. When you delete a[0] (which is 1), everything shifts down in the array. The correct thing to do would be to use a[0] again (which is now 2), but Ruby instead uses a[1] (which is now 3). Then a[1] (3) gets deleted, something new moves to a[1] (4), and Ruby skips that to do a[2] (5), and so on.

The general explanation is that modifying the array (or hash, or whatever) that you're enumerating over can cause unpredictable behaviour. This is true in many languages, not just Ruby, because detecting and correcting for modifications during such an iteration is a difficult problem in general. The safest thing to do (as you've discovered) is to make modifications to a separate array.

matthew smillie.

···

On Jul 2, 2006, at 17:07, Dark Ambient wrote:

I'm responding on my post above (re: why pic_names.delete(name), jumps
an element in the loop.
There must be a reason for this and still would like to some
elucidation on the matter.

However I found by creating a new array pix2bmoved =
and doing a pix2bmoved.push[name], things seem to work right.