-3

I have this code describing a context-free grammar and I'm trying to generate random strings that match it; for example, like this:

"John thinks that Mary hates every green cat"

But my current output is:

[['_S', ['_NP _VP']], ['_NP', ['_Det _Adj _N', '_Det _N', '_Adj _PropN', '_PropN']], ['_VP', ['_Vi', '_Vt _NP', '_Vc _Comp _S']]]
[['_Det', ['the', 'a', 'some', 'any', 'every']], ['_Adj', ['green', 'young', 'tired', 'confused']], ['_N', ['dog', 'cat']], ['_PropN', ['John', 'Mary']], ['_Vi', ['sleeps', 'walks']], ['_Vt', ['loves', 'hates']], ['_Vc', ['says', 'thinks', 'believes']], ['_Comp', ['that']]]

please help!

import random


psg_rules_str = "S → NP VP\n" \
                "NP → Det Adj N | Det N | Adj PropN | PropN\n" \
                "VP → Vi | Vt NP | Vc Comp S"

terminals_str = "Det → the | a | some | any | every\n" \
                "Adj → green | young | tired | confused\n" \
                "N → dog | cat\n" \
                "PropN → John | Mary\n" \
                "Vi → sleeps | walks\n" \
                "Vt → loves | hates\n" \
                "Vc → says | thinks | believes\n" \
                "Comp → that"

psg_rules_list = [a.split("→") for a in psg_rules_str.split("\n")]
for p in psg_rules_list:
    p[0] = "_" + p[0].strip()
    p[1] = p[1].split("|")
    p[1] = ["_" + a.strip().replace(" ", " _") for a in p[1]]
print(psg_rules_list)
# [['_S', ['_NP _VP']], ['_NP', ['_Det _Adj _N', '_Det _N', '_Adj _PropN', '_PropN']], ['_VP', ['_Vi', '_Vt _NP', '_Vc _Comp _S']]]

terminals_list = [a.split("→") for a in terminals_str.split("\n")]
for t in terminals_list:
    t[0] = "_" + t[0].strip()
    t[1] = t[1].split("|")
    t[1] = [a.strip() for a in t[1]]
print(terminals_list)
# [['_Det', ['the', 'a', 'some', 'any', 'every']], ['_Adj', ['green', 'young', 'tired', 'confused']], ['_N', ['dog', 'cat']], ['_PropN', ['John', 'Mary']], ['_Vi', ['sleeps', 'walks']], ['_Vt', ['loves', 'hates']], ['_Vc', ['says', 'thinks', 'believes']], ['_Comp', ['that']]]

def reachTerminals(from_nts, with_rules, with_ts):
    from_nts = str.upper("_" + from_nts.replace("_", "").strip().replace(" ", " _"))
    rule_tags = [a[0] for a in with_rules]
    ts_tags = [a[0] for a in with_ts]
    nts_todo = [a for a in rule_tags if a in from_nts]
    while nts_todo != list():
        tag = nts_todo[0]
        wr_index = rule_tags.index(tag)
        repl_choices = with_rules[wr_index][1]

        nts_todo = [a for a in rule_tags if a in from_nts]


sentence = reachTerminals(from_nts="s", with_rules=psg_rules_list, with_ts=terminals_list)
l_l_l_l_l_l_l_l
  • 528
  • 2
  • 8
python Q
  • 11
  • 1
  • Can you demonstrate *any* effort at solving this yourself? – Scott Hunter Apr 06 '20 at 00:16
  • 1
    what do you mean ? you mean you can help me with my Q? – python Q Apr 06 '20 at 00:19
  • 1
    @pythonQ It is normally expected that askers provide evidence/details of their efforts in solving the problem. Not only does this show personal investment of the same kind you would expect from potential answerers, but it also gives us an idea of your general level and of the best way to help you. – MikaelF Apr 06 '20 at 00:23
  • 1
    its my first time posting a question here, so I really dont know what are the requirements for it. I just need help with printing a statement that would help me achieve the goal – python Q Apr 06 '20 at 00:25
  • 1
    I should have said this first, but welcome to SO. You will find many people willing to help you here, but in general, it is always good to turn this question: "my code doesn't work, please help me." --> into this --> "I'm having issue X. I tried fixing it by Y, and here is the code Z that I have written so far to try and fix it.". This is a stronger base on which to build potential answers, and shows dedication to the issue. Sometimes questions look in the first form just look like someone copy-pasted their homework. – MikaelF Apr 06 '20 at 00:32
  • "I really don't know what are the requirements ..." Why do you not know? When you created your account, the welcome materials referred you to the `intro tour`, which includes those posting guidelines, and a *lot* of information we've developed over the years so first-timers *will* know what we expect. Take a breath, back up, go through the intro tour, and then pound this posting into shape to be a Stack Overflow question. – Prune Apr 06 '20 at 00:40
  • 1
    In particular, if you're looking for someone to help you figure out code design, or to hand-hold you through some short-cuts toward finishing your coding assignment, you're on the wrong site: "I don't know what to write next" is specifically *not* what we handle here. Review your educational materials, make an honest attempt at writing the code, and *then*, if you have troubles, we can often help *fix* code that *almost* works. – Prune Apr 06 '20 at 00:42
  • I feel offended by your comment and thank you for the clarification – python Q Apr 06 '20 at 00:46
  • 1
    Question needs to be renamed. Every question on Stackoverflow is about how to make the output match the desired output. – Neil Apr 06 '20 at 07:38

1 Answers1

0

You nearly have the program working. Here's a way to complete the reachTerminals function:

import random

psg_rules_str = "S → NP VP\n" \
                "NP → Det Adj N | Det N | Adj PropN | PropN\n" \
                "VP → Vi | Vt NP | Vc Comp S"

terminals_str = "Det → the | a | some | any | every\n" \
                "Adj → green | young | tired | confused\n" \
                "N → dog | cat\n" \
                "PropN → John | Mary\n" \
                "Vi → sleeps | walks\n" \
                "Vt → loves | hates\n" \
                "Vc → says | thinks | believes\n" \
                "Comp → that"

psg_rules_list = [a.split("→") for a in psg_rules_str.split("\n")]
for p in psg_rules_list:
    p[0] = "_" + p[0].strip()
    p[1] = p[1].split("|")
    p[1] = ["_" + a.strip().replace(" ", " _") for a in p[1]]

terminals_list = [a.split("→") for a in terminals_str.split("\n")]
for t in terminals_list:
    t[0] = "_" + t[0].strip()
    t[1] = t[1].split("|")
    t[1] = [a.strip() for a in t[1]]

def reachTerminals(from_nts, with_rules, with_ts):
    from_nts = str.upper("_" + from_nts.replace("_", "").strip().replace(" ", " _"))
    rule_tags = [a[0] for a in with_rules]
    ts_tags = [a[0] for a in with_ts]
    nts_todo = [a for a in rule_tags if a in from_nts]
    while nts_todo:
        for tag in nts_todo:
            wr_index = rule_tags.index(tag)
            repl_choices = with_rules[wr_index][1]

            choice = random.choice(repl_choices)
            from_nts = from_nts.replace(tag, choice, 1)
        nts_todo = [a for a in rule_tags if a in from_nts]

    ts_todo = [a for a in ts_tags if a in from_nts]
    while ts_todo:
        for tag in ts_todo:
            wr_index = ts_tags.index(tag)
            repl_choices = with_ts[wr_index][1]

            choice = random.choice(repl_choices)
            from_nts = from_nts.replace(tag, choice, 1)
        ts_todo = [a for a in ts_tags if a in from_nts]

    return from_nts


print(reachTerminals(from_nts = "s", with_rules = psg_rules_list, with_ts = terminals_list))

The important tools for you to use are the random.choice function and the str.replace function's third parameter, which lets you only replace the first occurrence of the substring. I haven't thoroughly tested the code, but it seems to be working as expected. Example outputs:

green John loves some confused dog

Mary says that the tired dog says that some green cat hates some cat

every green dog loves young John

John loves the tired cat

l_l_l_l_l_l_l_l
  • 528
  • 2
  • 8