I've been trying to build something similar to a breadth-first tree-like structure for a graph, which contains all possible paths from a given node. I didn't have problem with the algorithm as much as I do with some sort of error that pops up. Here's the relevant code below:
(set 'my-graph '((A (B C))
(B (D E))
(C (F G))
(D (E))
(E (H))
(F (H I))
(G (I))
(H (J))
(I (J))
(J ())))
(defun search-tree(graph traversed visited)
(cond
((null traversed) NIL)
(:else (let*
((new-visited (append visited (list (car traversed))))
(children (add-children graph (car traversed)
(append (cdr traversed) new-visited))))
(cond
((null children) (list (car traversed)))
(:else
(cons (car traversed)
(mapcar (lambda(x) (search-tree graph (list x) new-visited)) children)))
)
)
)
)
)
;;; Selects the node to pick returned children from
(defun add-children(graph node visited)
(cond
((null graph) NIL)
((equal (caar graph) node) (new-nodes (cadar graph) visited))
(:else (add-children (cdr graph) node visited))
)
)
;;; Returns new, unvisited nodes from the children of a node
(defun new-nodes(children visited)
(cond
((null children) NIL)
((member (car children) visited) (new-nodes (cdr children) visited))
(:else (cons (car children) (new-nodes (cdr children) visited)))
)
)
Function search tree is called as (search-tree my-graph '(A) '()) and it returns almost everything as I want correctly, but the first terminal node which is replaced with a # symbol (it should be (J)). What could be the problem in here? That's the returned value.
(A (B (D (E (H #))) (E (H (J)))) (C (F (H (J)) (I (J))) (G (I (J)))))
I've tried tracing the code, but I still don't understand why is the (J) list swapped in mid-recursion with a # symbol.
Usually I would guess that it has something to do with *print-level*.
This variable controls how deep nested lists are printed. Set it to a number for the level. Lists in a deeper level are replaced with a # character.
If setting that to NIL does not help, then you might also want to consult the Allegro CL manual - I can remotely remember that the IDE also has its own settings.
Related
I am studying NLP with Racket and Dr. Racket.
I am working with this code:
#lang racket
(define english-1
'((Initial (1))
(Final (9))
(From 1 to 3 by NP)
(From 1 to 2 by DET)
(From 2 to 3 by N)
(From 3 to 4 by BV)
(From 4 to 5 by ADV)
(From 4 to 5 by |#|)
(From 5 to 6 by DET)
(From 5 to 7 by DET)
(From 5 to 8 by |#|)
(From 6 to 7 by ADJ)
(From 6 to 6 by MOD)
(From 7 to 9 by N)
(From 8 to 8 by MOD)
(From 8 to 9 by ADJ)
(From 9 to 4 by CNJ)
(From 9 to 1 by CNJ)))
(define (getf x y)
(if (eq? (car x) y)
(cadr x)
(getf (cdr x) y)))
(define (initial-nodes network)
(list-ref (assoc 'Initial network) 1))
(define (final-nodes network)
(list-ref (assoc 'Final network) 1))
(define (transitions network)
(filter (lambda (x) (eq? (car x) 'From)) network))
(define (trans-node transition)
(getf transition 'From))
(define(trans-newnode transition)
(getf transition 'to))
(define (trans-label transition)
(getf transition 'by))
(define abbreviations
'((NP kim sandy lee)
(DET a the her)
(N consumer man woman)
(BV is was)
(CNJ and or)
(ADJ happy stupid)
(MOD very)
(ADV often always sometimes)))
(define (recognize network tape)
;; returns t if sucessfully recognizes tape - nil otherwise
(call/cc (lambda (return)
(define (recognize-next node tape network)
(if (and (null? tape) (member node (final-nodes network)))
(return #t) ; success
(for ([transition (transitions network)])
;; try each transition of the network
(when (equal? node (trans-node transition)) ; if it starts at the right node
(for ([newtape (recognize-move (trans-label transition) tape)])
;; try each possible new value of tape
(recognize-next (trans-newnode transition) newtape network))))))
(for ([initialnode (initial-nodes network)])
(recognize-next initialnode tape network))
null))) ; failed to recognize
(define (recognize-move label tape)
(if (or (eq? label (car tape))
(member (car tape) (assoc label abbreviations)))
(list (cdr tape))
(if (eq? label '|#|)
(list tape)
null)))
(require racket/trace)
(trace recognize-move)
(recognize-move english-1 '(hahaha))
The code seems to be mostly fine. However, I keep getting a error messaging related to the recognize-move function:
member: not a proper list: #f
And I thought I was dealing with lists... How can I solve this?
The problem is with this form:
(member (car tape) (assoc label abbreviations))
If assoc does not find anything the result is #f. (member 'anything #f) will not work. In Common Lisp false is the same as an empty list so member on false will work, but not in Scheme. You can perhaps make sure it's a list like this:
(member (car tape) (or (assoc label abbreviations) '()))
This is code translated from Common Lisp. In CL, nil is false and is also the empty list, (). In Racket, #f is false and is not the same as (). assoc wants to return false if it does not find a match: because of the way CL puns on false and the empty list, this means that (member ... (assoc ...)) will always work. In Racket it won't: you need to check to see if assoc failed to find a match.
What is the easiest way to accomplish the following in a Mathematica clone or in any version of Lisp(any language is probably okay actually even Haskell)? It doesn't appear any lisps have a similar replace function.
Replace[{
f[{x, "[", y, "]"}],
f#f[{x, "[", y, y2, "]"}]
}
, f[{x_, "[", y__, "]"}] :> x[y],
Infinity]
and a return value of {x[y], f[x[y, y2]]}
It replaces all instances of f[{x_, "[", y__, "]"}] in args where x_ represents a single variable and y__ represents one or more variables.
In lisp the function and replacement would probably be the equivalent(forgive me I am not the best with Lisp). I'm looking for a function of the form (replace list search replace).
(replace
'(
(f (x "[" y "]"))
(f (f '(x "[" y y2 "]")))
)
'(f (x_ "[" y__ "]"))
'(x y)
)
and get a return value of ((x y) (f (x y y2))).
Let's give it another try.
First, install quicklisp and use it to fetch, install and load optima and alexandria.
(ql:quickload :optima)
(ql:quickload :alexandria)
(use-package :alexandria)
The functions from alexandria referenced below are ensure-list and last-elt. If you don't have them installed, you can use the following definitions:
(defun ensure-list (list) (if (listp list) list (list list)))
(defun last-elt (list) (car (last list)))
We define rules as functions from one form to another.
Below, the function tries to destructure the input as (f (<X> "[" <ARGS> "]"), where <ARGS> is zero or more form. If destructuring fails, we return NIL (we expect non-matching filters to return NIL hereafter).
(defun match-ugly-funcall (form)
(optima:match form
((list 'f (cons x args))
(unless (and (string= "[" (first args))
(string= "]" (last-elt args)))
(optima:fail))
`(,x ,#(cdr (butlast args))))))
(match-ugly-funcall '(f (g "[" 1 3 5 4 8 "]")))
; => (G 1 3 5 4 8)
Then, we mimic Mathematica's Replace with this function, which takes a form and a list of rules to be tried. It is possible to pass a single rule (thanks to ensure-list). If a list of list of rules is given, a list of matches should be returned (to be done).
(defun match-replace (form rules &optional (levelspec '(0)))
(setf rules (ensure-list rules))
(multiple-value-bind (match-levelspec-p recurse-levelspec-p)
(optima:ematch levelspec
((list n1 n2) (if (some #'minusp (list n1 n2))
(optima:fail)
(values (lambda (d) (<= n1 d n2))
(lambda (d) (< d n2)))))
((list n) (if (minusp n)
(optima:fail)
(values (lambda (d) (= d n))
(lambda (d) (< d n)))))
(:infinity (values (constantly t) (constantly t))))
(labels
((do-replace (form depth)
(let ((result
(and (funcall match-levelspec-p depth)
(some (lambda (r) (funcall r form)) rules))))
(cond
(result (values result t))
((and (listp form)
(funcall recurse-levelspec-p depth))
(incf depth)
(do (newlist
(e (pop form) (pop form)))
((endp form) (values form nil))
(multiple-value-bind (result matchedp) (do-replace e depth)
(if matchedp
(return (values (nconc (nreverse newlist)
(list* result form)) t))
(push e newlist)))))
(t (values form nil))))))
(do-replace form 0))))
And a test:
(match-replace '(a b (f (x "[" 1 2 3 "]")) c d)
#'match-ugly-funcall
:infinity)
; => (A B (X 1 2 3) C D)
; T
In order to replace all expressions instead of the first matching one, use this instead:
(defun match-replace-all (form rules &optional (levelspec '(0)))
(setf rules (ensure-list rules))
(multiple-value-bind (match-levelspec-p recurse-levelspec-p)
(optima:ematch levelspec
((list n1 n2) (if (some #'minusp (list n1 n2))
(optima:fail)
(values (lambda (d) (<= n1 d n2))
(lambda (d) (< d n2)))))
((list n) (if (minusp n)
(optima:fail)
(values (lambda (d) (= d n))
(lambda (d) (< d n)))))
(:infinity (values (constantly t) (constantly t))))
(labels
((do-replace (form depth)
(let ((result
(and (funcall match-levelspec-p depth)
(some (lambda (r) (funcall r form)) rules))))
(cond
(result result)
((and (listp form)
(funcall recurse-levelspec-p depth))
(incf depth)
(mapcar (lambda (e) (do-replace e depth)) form))
(t form)))))
(do-replace form 0))))
Oh boy, how Mathematica manages to obfuscate everything by applying its renown NIH approach.
Basically, you're looking for a function to perform string replacement according to some pattern. In most languages, this is accomplished with regular expressions.
For instance, in Common Lisp using the cl-ppcre library it will look something like this:
(cl-ppcre:regex-replace-all
;; regular expression you match against with groups
"f\\[{(x[^ ]*), \"\\[\", ((y[^ ]* ?)+), \"\\]\"}\\]"
;; your string
"{f[{x, \"[\", y, \"]\"}], f#f[{x, \"[\", y, y2, \"]\"}]}"
;; substitution expression using groups 1 & 2
"\\1[\\2]")
Surely, you can write a specialized 20-line function for this problem of matching and substituting subtrees using subst and recursion, but if all that you want is cases similar to the presented one you can get away with a simple regex-based approach.
I would like to get the position of any element in the list and get nil if the element isn't in the list. I did:
(defun myposition (letter list)
(cond
((atom list) nil)
((equal (car list) letter) 0)
((null (car list)) (myposition letter))
(t (1+ (myposition letter (cdr list)))) ) )
(myposition 'k '(g h i j k l)
4
(myposition 'p '(g h i j k l)
nil is not a number
When I replace ((atom list) nil) par ((atom list) 0), I get 6 instead nil
(myposition 'p '(g h i j k l)
6
In your first example, your function will recursively compute 1+1+1+1+0 = 4 to find the correct result.
In your second example, it will run through the whole list, add 1 per (non-matching) element, and finally add nil. So it actually computes 1+1+1+1+1+1+nil, which is incorrect since nil is not a number, hence the error message. If you replace nil by zero, it computes 1+1+1+1+1+1+0 which is wrong.
So your basic problem is that you recursively add 1 and, reaching the end of the list, you would like to throw away what you computed until then. But you have an addition pending which you cannot escape.
The easiest way is to change from a recursive to a tail-recursive solution, which is technically a plain goto. Here the addition is done by incrementing a variable, not by unwinding the call stack, which makes it easy to throw away the result from the previous additions and just return nil because there is no addition pending.
A (tail-)recursive solution could be:
(defun myposition (letter lst)
(labels ((sub (lst pos)
(cond
((null lst) nil)
((equal (car lst) letter) pos)
(t (sub (cdr lst) (1+ pos))))))
(if (atom lst) nil (sub lst 0))))
This will work in Common Lisp, but technically, if your implementation does no tail call optimisation, it might still blow the stack for large lists. That's why Common Lisp prefers iterative solutions, such as using the loop macro:
(defun myposition (letter lst)
(when (consp lst)
(loop for c in lst for i from 0
when (equal c letter) return i)))
I am trying to modify the cdr of the car of an element of a list using set!, but I am getting an error: "set! not an identifier". Can anyone explain to me why this is happening? I am starting to work with objects by the way.
#lang racket
(define (multiset)
(let ((main-list '()))
(define (empty)
(eq? main-list '()))
(define (insert x)
(cond ((empty)
(set! main-list (cons (cons x 1) main-list)))
((= (car (car main-list)) x)
(begin (set! (cdr (car main-list))
(+ 1 (cdr (car main-list))))))
(else (cdr main-list))))
A multiset is represented as a list of pairs. For example, if I had a list '(1 1 1 2 2 2), the multiset representation would be '((1.3)(2.3))
The syntax of set! is
(set! <identifier> <expression>)
which is to say that the first form must be a symbol. In your code you are using:
(cdr (car main-list))
as the 'identifier' - hence the error.
Perhaps your background is CommonLisp and you are expecting set! to behave like setf? In Scheme there are separate functions for setting the 'car' and 'cdr' of a pair. Use set-car! and set-cdr!
> (define pair (cons 'car 'cdr))
> pair
(car . cdr)
> (set-car! pair 'foo)
> pair
(foo . cdr)
> (set-cdr! pair 'bar)
> pair
(foo . bar)
In R6RS (and probably R7RS) set-car! and set-cdr! can be found in the (rnrs mutable-pairs) library
GoZoner has given you the right explanation, but it is perfectly possible (and desirable) do avoid set! procedures. Here's an example of a procedure having the same result:
(define (rle lst)
(define (newpair c l res)
(if (> l 0) (cons (cons c l) res) res))
(let loop ((lst lst) (c #f) (l 0) (res '()))
(if (null? lst)
(reverse (newpair c l res))
(let ((n (car lst)))
(if (equal? c n)
(loop (cdr lst) c (add1 l) res)
(loop (cdr lst) n 1 (newpair c l res)))))))
such as
(rle '(1 1 1 2 2 2))
=> '((1 . 3) (2 . 3))
I'm just starting with Scheme.
I'm trying to use some procedures from String Library.
Here's what I need:
input: "ccaAaAaAa"
function: generate all strings substituting all possible aAa to aBa, one substitution only
output: "ccaBaAaAa" and "ccaAaBaAa" and "ccaAaAaBa"
Is there any easy way to do that? Maybe a procedure that return a list of index of pattern found?
Apparently the searching function string-contains only returns the first occurrence.
What I thought is: after producing the first string "ccaBaAaAa", trim to the first index of the pattern found: the original "ccaAaAaAa" becomes "AaAaAa". Repeat (recursively).
Thanks.
string-contains won't give you a list of all occurrences of the substring, but it will tell you whether there is one, and if there is, what its index is. It also allows you to restrict the search to a particular range within the string. Based on this, if you get a match, you can recursively search the rest of the string until you no longer get a match.
From there, you can do the substitution for each match.
What is wrong by writing such a function?
(define (replace input)
(let loop ((done '())
(remaining (string->list input))
(output '()))
(if (pair? remaining)
(if (char=? #\a (car remaining))
(let ((remaining (cdr remaining)))
(if (pair? remaining)
(if (char=? #\A (car remaining))
(let ((remaining (cdr remaining)))
(if (pair? remaining)
(if (char=? #\a (car remaining))
(loop (append done (list #\a #\A))
remaining
(cons (list->string
(append done
(cons #\a
(cons #\B
remaining))))
output))
(loop (append done (list #\a #\A
(car remaining)))
(cdr remaining)
(reverse output)))
(reverse output)))
(loop (append done (list #\a (car remaining)))
(cdr remaining)
(reverse output)))
(reverse output)))
(loop (append done (list (car remaining)))
(cdr remaining)
(reverse output)))
(reverse output))))
(replace "ccaAaAaAa") ;=> ("ccaBaAaAa" "ccaAaBaAa" "ccaAaAaBa")
About 15 minutes work.
I thought there could be better string libraries that I didn't know about. But I end up doing what I'd proposed in the question. (For a general input case)
(define (aplicarRegra cadeia cadeiaOriginal regra n)
(let* ((antes (car regra))
(depois (cdr regra))
(index (string-contains cadeia antes))
(tamanho (string-length antes))
(diferenca (- (string-length cadeiaOriginal) (string-length cadeia))))
(if index
(let* ((cadeiaGerada (string-replace cadeiaOriginal depois (+ index diferenca) (+ index diferenca tamanho))))
(if(<= (string-length cadeiaGerada) n)
(lset-union equal? (list cadeiaGerada) (aplicarRegra(substring cadeia (+ 1 index)) cadeiaOriginal regra n))
(aplicarRegra (substring cadeia (+ 1 index)) cadeiaOriginal regra n)))
(list))))
But thanks anyway!