I’m working on context-free grammars, and I have a function that returns the (terminal values of grammar)
for example:
i have non-terminal function that results in (A B) , from calling say ((A cat) (B happy np) (A B sad))
so technically A and B are non terminals of the grammar. Now I want to be able to get the terminals (cat happy np sad)
(define terminals
(lambda (lsts)
(cond
((null? lsts) lsts)
((not(member? (car(car lsts)) (n-terminals lsts)))
(cons (car(car lsts)) (terminals (car (cdr lsts)))))
(else (terminals (cdr lsts))))))
PS: functionality of n-terminals is described above.
member? is a boolean function that returns true if an item is a member of the list, false otherwise.
My function returns an empty lst. What am I missing here?
The problem is with what you do when you find a terminal-symbol. There are really two separate problems here.
First, you only ever look at the first symbol for each rule. Your
lstsvariable is a list of rules; each rule is a list of symbols. So(car (car lsts))gets you the first symbol of the first rule.The second problem is how you recurse after you’ve found a terminal symbol. Normally, you pass
terminalsa list of lists. However, in the case after you’ve found a terminal symbol (that is, the one starting with(member? ...)), you pass it(car (cdr lsts)). We knowlstsis a list of lists. So(cdr lsts)is also a list of lists (or an empty list, of course). This means(car (cdr lsts))is just a normal list.I think the simplest way to fix this problem would be to break it up into two functions. First, write a function that gets the terminals from a single rule. That is, instead of focusing on the whole input
((A cat) (B happy np) (A B sad)), focus on one rule at a time like(B happy np). So write a function that, given(B happy np), gives you(happy np). Next, write a function that applies this function on every rule inlstsand combines all the output. (You might find theappendfunction useful here.)Writing it in two separate functions should make the problem easier to consider. The resulting code will also be simpler and easier to read, which is a bonus.
Also, an unrelated matter of style: you can (and should) write
as
These two expressions have the same meaning, but the second one is easier to read and used more often.
EDIT: As I said in the comment, I would calculate the list of non-terminals first and use that for the function that does one line at a time. You can make this simple to use by nesting your two functions and taking advantage of Scheme’s lexical scoping:
Another option would be to break it out into two functions that aren’t nested and pass in a list of the non-terminal symbols: