notany: how to check that a char is not in a list

18 views
Skip to first unread message

Mr.SpOOn

unread,
Oct 27, 2009, 10:56:29 AM10/27/09
to py...@googlegroups.com
Hi,
I have this backward chaining rule:


sharps
use accidentals_count($accidentals, $accidentals_count)
when
$accidentals_count = 0
notany
'b' in $accidentals
$accidentals_count = $accidentals_count + $accidentals.__len__()

I have also another rule which is the same as this, but inside the
"notany" has '#' in $accidentals and the len should be multiplied by
"-1", this way: ($accidentals.__len__() * (-1))

$accidentals should contain a certain number of 'b' or '#'. I mean, it
may be: 'bbbb', or '##', '#######', but not 'b##'.
So I want to check that they are all the same and in the end I need the count.

I think I'm doing something wrong with the notany.
Any suggestion?

Thanks,
Carlo

Bruce Frederiksen

unread,
Oct 27, 2009, 2:04:58 PM10/27/09
to py...@googlegroups.com
Hi Carlo!

Good to see you making progress with your music program on Pyke!

To answer your question, Python has a nice 'count' method on strings:


sharps
   use accidentals_count($accidentals, $accidentals_count)
   when
      $accidentals_count = $accidentals.count('#')
      check $accidentals_count == len($accidentals)

For the flats, I'd store the count of the 'b' characters in a variable other than $accidentals_count (say $b_count) and then have:

      $accidentals_count = -$b_count

at the end of the rule (after the check).

If you use $accidentals_count for the $b_count, then you won't be able to change it's value at the end to negate it.  Rather, the rule will fail (unless the count is zero), because the result of the negation will not match the value already assigned to $accidentals_count.  What happens is that the value computed by Python on the right side of the '=' is pattern matched against the pattern on the left side.  If the left side is an unbound variable, the variable is bound to the value.  If the left side is a bound variable, then the two values must match for the line to succeed.

Hope this helps!

-Bruce

Mr.SpOOn

unread,
Oct 27, 2009, 2:25:48 PM10/27/09
to py...@googlegroups.com
2009/10/27 Bruce Frederiksen <dang...@gmail.com>:

> Hi Carlo!
>
> Good to see you making progress with your music program on Pyke!
>
> To answer your question, Python has a nice 'count' method on strings:

Thanks.
I think I cannot use the "count" method, because $accidentals is a tuple.

It's a tuple because in the top level rule I have a string like this:
C## (that may have 0, 1 or more #), then I divide it into a tuple and
put the "C" in a variable and the rest in *$accidentals.

Do you suggest to transform it again in a list or something, or shall
I just use the __len__() method?

>
> sharps
>    use accidentals_count($accidentals, $accidentals_count)
>    when
>       $accidentals_count = $accidentals.count('#')
>       check $accidentals_count == len($accidentals)

Great, this is very clever :D


> For the flats, I'd store the count of the 'b' characters in a variable other
> than $accidentals_count (say $b_count) and then have:
>
>       $accidentals_count = -$b_count
>
> at the end of the rule (after the check).
>
> If you use $accidentals_count for the $b_count, then you won't be able to
> change it's value at the end to negate it.  Rather, the rule will fail
> (unless the count is zero), because the result of the negation will not
> match the value already assigned to $accidentals_count.  What happens is
> that the value computed by Python on the right side of the '=' is pattern
> matched against the pattern on the left side.  If the left side is an
> unbound variable, the variable is bound to the value.  If the left side is a
> bound variable, then the two values must match for the line to succeed.

I think I'll need some time to get this, but thanks a lot.

Bye,
Carlo

Bruce Frederiksen

unread,
Oct 27, 2009, 3:03:47 PM10/27/09
to py...@googlegroups.com
On Tue, Oct 27, 2009 at 2:25 PM, Mr.SpOOn <mr.sp...@gmail.com> wrote:

2009/10/27 Bruce Frederiksen <dang...@gmail.com>:
> Hi Carlo!
>
> Good to see you making progress with your music program on Pyke!
>
> To answer your question, Python has a nice 'count' method on strings:

Thanks.
I think I cannot use the "count" method, because $accidentals is a tuple.

It's a tuple because in the top level rule I have a string like this:
C## (that may have 0, 1 or more #), then I divide it into a tuple and
put the "C" in a variable and the rest in *$accidentals.

Do you suggest to transform it again in a list or something, or shall
I just use the __len__() method?

You're right, tuples don't have a count method.  But lists do.  So you could either convert it to a list:

      $accidental_count = list($accidentals).count('#')

or convert it back into a string:

      $accidental_count = ''.join($accidentals).count('#')

you could still compare against len($accidentals) as that would be the same whether it's a tuple, list or string.

-Bruce

Mr.SpOOn

unread,
Oct 27, 2009, 5:02:14 PM10/27/09
to py...@googlegroups.com
2009/10/27 Bruce Frederiksen <dang...@gmail.com>:

> You're right, tuples don't have a count method.  But lists do.  So you could
> either convert it to a list:
>
>       $accidental_count = list($accidentals).count('#')

Yes, I solved with this.

Anyway, there's a problem with this accidental_count. I didn't see it
before, but in the way I wrote the rules, when I try to prove the goal
from the top level rule it just tries to prove it with the first rule
in the code. This is because they have the same "use" line, which is:

use accidentals_count($accidentals, $accidentals_count)

I thought I could write that like this:

use accidentals_count(('b', *$accidentals), $accidentals, $accidentals_count)

and

use accidentals_count(('#', *$accidentals), $accidentals, $accidentals_count)

but it doesn't seem to work. I thought it should work, because I call
it in this way:

accidentals_count($accidentals, $accidentals_count)

and at this point $accidentals is this: ('#', '#')

Mr.SpOOn

unread,
Oct 27, 2009, 5:12:23 PM10/27/09
to py...@googlegroups.com
2009/10/27 Mr.SpOOn <mr.sp...@gmail.com>:

> but it doesn't seem to work. I thought it should work, because I call
> it in this way:
>
> accidentals_count($accidentals, $accidentals_count)

Ok, one error is here, because it should be something like:

accidentals_count($accidentals, $acc, $accidentals_count)

Still there's something wrong. The trick of putting the '#' or 'b' to
have a match removes an element, so the count is wrong. I think I can
just add one, but maybe there's something else.

Bruce Frederiksen

unread,
Oct 28, 2009, 7:22:47 AM10/28/09
to py...@googlegroups.com
Having the same "use" clause in both rules (sharps and flats) should be OK.  For example, with the following rule:


sharps
    use accidentals_count($accidentals, $accidentals_count)
    when
        $accidentals_count = list($accidentals).count('#')
        check $accidentals_count == len($accidentals)

When pyke tries this rule with $accidentals set to ('b', 'b'), it will first set $accidentals_count to 0, and then the check will fail because 0 is not == to len($accidentals) (which is 2).  As there is no alternate solution to the first line on backtracking, the rule fails.  Pyke will then try the next rule, which should be the flats rule:

flats
    use accidentals_count($accidentals, $accidentals_count)
    when
        $b_count = list($accidentals).count('b')
        check $b_count == len($accidentals)
        $accidental_count = -$b_count

This sets $b_count to 2, the "check" line then succeeds, $accidental_count is set to -2 and the rule succeeds.

I would think that if you had a problem, you have a misspelling somewhere, or you copied the sharps rule to make the flats rule and didn't change the first two $accidental_count variables to $b_count, or didn't change '#' to 'b' or something like that.

-Bruce
Reply all
Reply to author
Forward
0 new messages