Asserting facts based on parsing

22 views
Skip to first unread message

Mr.SpOOn

unread,
Sep 10, 2009, 11:27:27 AM9/10/09
to py...@googlegroups.com
Hi,
I'm trying to write a rule base that asserts facts based on the
parsing of some input string.

I need to use this to find the notes composing a chord starting from
its name. For example, if I give just 'C' as input, it should
recognize that it is a major chord and then use the relative rule for
the major chord.

This is just the simplest case, because chord names can be very
complex, so they need some parsing. I can have Cm for minor chords, or
C7, or Cmaj7, or Cm7.

I think it is pretty easy to parse these strings in pure python, but I
wonder if I can do the same using a rule base. What do you think? Any
suggestion?

Bruce Frederiksen

unread,
Sep 11, 2009, 11:49:55 AM9/11/09
to py...@googlegroups.com
Hi Mr.SpOOn,

An easy way to do this would be to convert the string to a tuple of individual characters:

>>> tuple('Cmaj7')
('C', 'm', 'a', 'j', '7')

Then you can use the tuple patterns against it.

-Bruce

Mr.SpOOn

unread,
Sep 11, 2009, 4:12:08 PM9/11/09
to py...@googlegroups.com
2009/9/11 Bruce Frederiksen <dang...@gmail.com>:

> Hi Mr.SpOOn,
>
> An easy way to do this would be to convert the string to a tuple of
> individual characters:
>
>>>> tuple('Cmaj7')
> ('C', 'm', 'a', 'j', '7')
>
> Then you can use the tuple patterns against it.

Well, yes. But I think that doing so I have to write the rules for
every different type of chord.

For example, there are this chords:
C
C-
C 7
C- 7
C 9
C- 9

In that way I'll have to write five or six patterns to match the
different chords, while it should recognize that it is a minor chord
(with the symbol -) and then the other part based on the number.

There is an order in the symbols and numbers that may follow the chord
name, but I'm not sure if this can be coded with chaining rules.

Bruce Frederiksen

unread,
Sep 20, 2009, 12:54:56 PM9/20/09
to py...@googlegroups.com
Yes, you would need several rules.  Something like:

top_level
    use chord($name, $fundamental, $major_minor, $type)
    when
        $chars = tuple($name)
        note($chars, $fundamental, $rest1)
        major_minor($rest1, $major_minor, $rest2)
        chord_type($rest2, $type)

note_rule
    use note(($note, *$rest), $note, $rest)

major_minor_1
    use major_minor(('m', 'a', 'j', *$rest), major, $rest)

major_minor_2
    use major_minor(('m', 'i', 'n', *$rest), minor, $rest)

chord_type_simple
    use chord_type((), simple)

chord_type_7th
    use chord_type(('7'), seventh)

This isn't so bad, is it?

-Bruce

Mr.SpOOn

unread,
Sep 24, 2009, 7:17:52 AM9/24/09
to py...@googlegroups.com
Oh, great. This is really helpful and I think that with the right
rules I can derive all chords.

If I wanted to prove the goal for a chord sequence (I mean, some
chords in a list) what is the best way to do this? Shall I call the
prove_1 method inside a for loop or is it better to iterate the list
inside the rule base?

Thanks,
Carlo

2009/9/20 Bruce Frederiksen <dang...@gmail.com>:

Bruce Frederiksen

unread,
Sep 24, 2009, 9:37:23 AM9/24/09
to py...@googlegroups.com
On Thu, Sep 24, 2009 at 7:17 AM, Mr.SpOOn <mr.sp...@gmail.com> wrote: 
If I wanted to prove the goal for a chord sequence (I mean, some
chords in a list) what is the best way to do this? Shall I call the
prove_1 method inside a for loop or is it better to iterate the list
inside the rule base?

You could do it either way.  The two reasons to do it within the rule base would be:
  1. If these chord sequences are only a part of some larger reasoning process, such that the whole sequence would be embedded within higher-level rules.
  2. If you wanted to use rules to generate chord sequences where you might find that the later chords in the sequence don't work with the rest of the material and the rules must backtrack to generate alternate chord sequences until a suitable sequence is found.  In this case, you would need to code the production of the chord sequence recursively.
Coding the rules recursively looks like:

sequence_done
    use sequence((), ())

sequence_1_more
    use sequence(($chord_template, *$rest), ($chord, *$chords))
    when
        generate_chord($chord_template, $chord)
        sequence($rest, $chords)

This will backtrack up the sequence trying alternate generate_chord solutions for each $chord_template in the input sequence.  (I'm sorry that I don't know enough music theory to be able to come up with better examples).

If you code the rule iteratively, this doesn't happen.  The entire sequence will be backtracked over in one step with no alternate solutions to individual members of the sequence tried:

iterative_sequence
    use sequence($input, $output)
    when
        python output_list = []
        forall
            $chord_template in $input
        require
            generate_chord($chord_template, $chord)
            python output_list.append($chord)
        $output = tuple(output_list)

The reason for using the iterative approach is that it will run somewhat faster and it doesn't grow the stack.  Using the recursive approach, you may hit Python's "recursion limit" for longer sequences.  (Of course, this limit can be raised by calling sys.setrecursionlimit).

I think that the reasons for just doing the iteration in python by repeated calls to prove_1 would be if that makes sense within the structure of the python program calling pyke.

Hope this helps!

-Bruce

Mr.SpOOn

unread,
Sep 24, 2009, 6:59:05 PM9/24/09
to py...@googlegroups.com
2009/9/24 Bruce Frederiksen <dang...@gmail.com>:

> Coding the rules recursively looks like:
>
> sequence_done
>     use sequence((), ())
>
> sequence_1_more
>     use sequence(($chord_template, *$rest), ($chord, *$chords))
>     when
>         generate_chord($chord_template, $chord)
>         sequence($rest, $chords)

This may be useful later, but I need to look a lot more into it to
really get it.

Anyway, the iterative approach solved another doubt, so to find the
type of a chord I'm doing like this:

top_level
use chord($name, $root, $output)
when
$chars = tuple($name)
note($chars, $root, $type)
type_sequence($type, $output)

type_sequence_1_more
use type_sequence($input, $output)
when
python type_list = []
forall
$type in $input
require
chord_type($type, $out_type)
python type_list.append($out_type)
$output = tuple(type_list)

note_rule
use note(($note, *$rest), $note, $rest)

major
use chord_type('M', major)
minor
use chord_type('m',minor)

seventh
use chord_type('7', seventh )


This is still incomplete (and I have to choose more appropriate names
for variables), since I need some other rules, but that should not be
a problem.
I don't know wether this is the right approach, but in this way the
type part may be as long as I need and I have the resulting type in a
single tuple. This was just to let you know.

Thanks again for your precious help,
Carlo

Reply all
Reply to author
Forward
0 new messages