Search function in lisp - search

How can i retrieve a list of items (string) which contain a specific word from another list. Here is an Example :
(setq l '("word1_jj" "word2_mm" "word3_jj" "word4_kk"))
I wanna extract all string in which figure "_jj.

You should make ends-with-p that takes the word and an ending. To do that you find out how many characters there is in the two strings and use subseq to make a string consisting of the last letters of the word. You can use equal to check it with the supplied argument it should match.
When you have that you can do this:
(remove-if-not (lambda (x) (ends-with-p x "_jj"))
'("word1_jj" "word2_mm" "word3_jj" "word4_kk"))
; ==> ("word1_jj" "word3_jj")
Alternatively you could make a make-end-predicate that returns a lambda that takes a word:
(remove-if-not (make-end-predicate "_jj")
'("word1_jj" "word2_mm" "word3_jj" "word4_kk"))
; ==> ("word1_jj" "word3_jj")

Related

In DrRacket how do I check if a string has a certain amount of characters, as well how do I determine what the first character in a string is

Basically I have a problem, here is the information needed to solve the problem.
PigLatin. Pig Latin is a way of rearranging letters in English words for fun. For example, the sentence “pig latin is stupid” becomes “igpay atinlay isway upidstay”.
Vowels(‘a’,‘e’,‘i’,‘o’,and‘u’)are treated separately from the consonants(any letter that isn’t a vowel).
For simplicity, we will consider ‘y’ to always be a consonant. Although various forms of Pig Latin exist, we will use the following rules:
(1) Words of two letters or less simply have “way” added on the end. So “a” becomes “away”.
(2) In any word that starts with consonants, the consonants are moved to the end, and “ay” is added. If a word begins with more than two consonants, move only the first two letters. So “hello” becomes “ellohay”, and “string” becomes “ringstay”.
(3) Any word which begins with a vowel simply has “way” added on the end. So “explain” becomes “explainway”.
Write a function (pig-latin L) that consumes a non-empty (listof Str) and returns a Str containing the words in L converted to Pig Latin.
Each value in L should contain only lower case letters and have a length of at least 1.
I understand that i need to set three main conditions here, i'm struggling with Racket and learning the proper syntax to write out my solutions. first I need to make a conditions that looks at a string and see if it's length is 2 or less to meet the (1) condition. For (2) I need to look at the first two characters in a string, i'm assuming I have to convert the string into a list of char(string->list). For (3) I understand I just have to look at the first character in the string, i basically have to repeat what I did with (2) but just look at the first character.
I don't know how to manipulate a list of char though. I also don't know how to make sure string-length meets a criteria. Any assistance would be appreciated. I basically have barely any code for my problem since I am baffled on what to do here.
An example of the problem is
(pig-latin (list "this" "is" "a" "crazy" "exercise")) =>
"isthay isway away azycray exerciseway"
The best strategy to solve this problem is:
Check in the documentation all the available string procedures. We don't need to transform the input string to a list of chars to operate upon it, and you'll find that there are existing procedures that meet all of our needs.
Write helper procedures. In fact, we only need a procedure that tells us if a string contains a vowel at a given position; the problem states that only a-z characters are used so we can negate this procedure to also find consonants.
It's also important to identify the best order to write the conditions, for example: conditions 1 and 3 can be combined in a single case. This is my proposal:
(define (vowel-at-index? text index)
(member (string-ref text index)
'(#\a #\e #\i #\o #\u)))
(define (pigify text)
; cases 1 and 3
(cond ((or (<= (string-length text) 2)
(vowel-at-index? text 0))
(string-append text "way"))
; case 2.1
((and (not (vowel-at-index? text 0))
(vowel-at-index? text 1))
(string-append (substring text 1)
(substring text 0 1)
"ay"))
; case 2.2
(else
(string-append (substring text 2)
(substring text 0 2)
"ay"))))
(define (pig-latin lst)
(string-join (map pigify lst)))
For the final step, we only need to apply the pigify procedure to each element in the input, and that's what map does. It works as expected:
(pig-latin '("this" "is" "a" "crazy" "exercise"))
=> "isthay isway away azycray exerciseway"

what is the interactive REPL IO function?

I have been learning Common Lisp for a while, there was a question I have met that
how I can implement such a function which allows user to input some words until user input exit.
(actually I want to know what kind of command line interactive function APIs fit such requirement)
e.g.
prompt "please input a word: " in the REPL, then store user inputs into a global my-words , exit when user input "exit".
You specification is a little bit incomplete (e.g. what constitutes a word in your problem? What if the user add multiple words? What if the input is empty?). Here below I am using CL-PPCRE to split the input into different words and add them all at once, because it seems useful in general. In your case you might want to add more error checking.
If you want to interact with the user, you should read and write from and to the *QUERY-IO* stream. Here I'll present a version with a global variables, as you requested, as well as another one without side-effects (apart from input/output).
With a global variable
Define the global variable and initialize it with an empty adjustable array.
I am using an array so that it is easy to add words at the end, but you could also use a queue.
(defvar *my-words* (make-array 10 :fill-pointer 0 :adjustable t))
The following function mutates the global variable:
(defun side-effect-word-repl ()
(loop
(format *query-io* "~&Please input a word: ")
(finish-output *query-io*)
(let ((words (ppcre:split
'(:greedy-repetition 1 nil :whitespace-char-class)
(read-line *query-io*))))
(dolist (w words)
(when (string-equal w "exit") ; ignore case
(return-from side-effect-word-repl))
(vector-push-extend w *my-words*)))))
The LOOP uses the simple syntax where there are only expressions and no loop-specific keywords. I first write the prompt to *QUERY-IO*. The ~& FORMAT directive performs the same operation as FRESH-LINE. As Rainer pointed out in comments, we have to call FINISH-OUTPUT to ensure the message is effectively printed before the user is expected to reply.
Then, I read a whole line from the same bidirectional stream, and split it into a list of words, where a word is a string of non-whitespace characters.
With DOLIST, I iterate over the list and add words into the global array with VECTOR-PUSH-EXTEND. But as soon as I encouter "exit", I terminate the loop; since I rely on STRING-EQUAL, the test is done case-insensitively.
Side-effect free approach
Having a global variable as done above is discouraged. If you only need to have a prompt which returns a list of words, then the following will be enough. Here, I use the PUSH/NREVERSE idiom to built the resulting list of words.
(defun pure-word-repl ()
(let ((result '()))
(loop
(format *query-io* "~&Please input a word: ")
(finish-output *query-io*)
(let ((words (ppcre:split
'(:greedy-repetition 1 nil :whitespace-char-class)
(read-line *query-io*))))
(dolist (w words)
(when (string-equal w "exit")
(return-from pure-word-repl (nreverse result)))
(push w result))))))
Note about words
As jkiiski commented, it might be better to split words at :word-boundary. I tried different combinations and the following result seems satisfying with weird example strings:
(mapcan (lambda (string)
(ppcre:split :word-boundary string))
(ppcre:split
'(:greedy-repetition 1 nil :whitespace-char-class)
"amzldk 'amlzkd d;:azdl azdlk"))
=> ("amzldk" "'" "amlzkd" "d" ";:" "azdl" "azdlk")
I first remove all whitespaces and split the string into a list of strings, which can contain punctuation marks. Then, each string is itself splitted at :word-boundary, and concatenated with MAPCAN to form a list of separate words. However, I can't really guess what your actual needs are, so you should probably define your own SPLIT-INTO-WORDS function to validate and split an input string.
CL-USER 23 > (progn
(format t "~%enter a list of words:~%")
(finish-output)
(setf my-words (read))
(terpri))
enter a list of words:
(foo bar baz)
or
CL-USER 28 > (loop with word = nil
do
(format t "~%enter a word or exit:~%")
(finish-output)
(setf word (read))
(terpri)
until (eql word 'exit)
collect word)
enter a word or exit:
foo
enter a word or exit:
bar
enter a word or exit:
baz
enter a word or exit:
exit
(FOO BAR BAZ)

Change list elements to a string with spaces in Prolog

In Prolog, if I am taking a list such as [hello,this,is,a,sentence] as a parameter for a predicate, how do I get a return value Y such that it will return that list as a string with spaces? e.g. [hello,this,is,a,sentence] will return hello this is a sentence.
makesentence([H|T],Y):- % some code here
I was able to run through the list recursively and have Y return the same list input with this:
makesentence([],[]). % base case returns an empty list
makesentence([X],[X]). % one list element returns that element in a list
makesentence([H|T],Y):- % a list of more than one element
makesentence(T,Result), % recursively call function on the tail
append([H],Result,Y). % append the head to the rest of the list
But when I try to make the output without a list and with spaces I faulter. I have tried this:
makesentence([],'').
makesentence([X],X).
makesentence([H|T],Y):-
makesentence(T,Result),
append(H,Result,Y).
I think it has to do with the fact the append predicate in Prolog only deals with appending a list, but I am unsure. How would I procede? Thanks in advance.
SWI-Prolog has a specialized built-in for that: atomic_list_concat/3
?- atomic_list_concat([hello,this,is,a,sentence],' ',A).
A = 'hello this is a sentence'.
figured it out with the help of Daniel. To put a list into a string with spaces use atomics_to_string/3. In my case:
makesentence([X],X).
makesentence([H|T],Y):-
makesentence(T,Result),
atomics_to_string([H,Result],' ',Y).
In the line atoms_to_string([H,Result],' ',Y)., the first parameter is the list, the second is what I want to add in between each entry, in this case a space ' ' and the third parameter is the assignment of the output, which in my case is Y. Thanks to Daniel for pointing me in the right direction.

Haskell counting words containing specific characters in strings

is it possible to get the number of words which contain a specific character from a string ?
for example: string = "yes no maybe"
it would return 2 if the specific character was 'e'.
I have been trying for hours :(
thanks
One way of doing this (shown as a GHCi session) is:
λ> let w = "yes no maybe"
λ> length $ filter (elem 'e') (words w)
2
We split the string into words using the words function.
Then filter the list of words using elem and our chosen character.
Then finally count the number of words that contained the character, using length.
Can also be written as:
length . filter (elem 'e') $ words w
composing the length and filter functions, then applying the combined function to the list of words.

Call function based on a string

I am passing in command line arguments to my Lisp program and they are formatted like this when they hit my main function:
("1 1 1" "dot" "2 2 2")
I have a dot function (which takes two vectors as arguments) and would like to call it directly from the argument, but this isn't possible because something like (funcall (second args)...) receives "dot" and not dot as the function name.
I tried variations of this function:
(defun remove-quotes (s)
(setf (aref s 0) '""))
to no avail, before realizing that the quotes were not really a part of the string. Is there a simple way to do this, or should I just check each string and then call the appropriate function?
"1 1 1" is a string of five characters: 1, space, 1, space and 1. The double quotes are not part of the string.
("1 1 1" "dot" "2 2 2") is a list of three strings.
There are no " characters above. The " are used to delimit strings in s-expressions.
If you have a dot function you need to tell us what kind of input data it expects.
Does it expect two lists of numbers? Then you have to convert the string "1 1 1" into a list of numbers.
(with-input-from-string (in "1 1 1")
(loop for data = (read in nil in)
until (eq data in)
collect data)))
To get the function DOT from the string "dot" first find the symbol DOT and then get its symbol function.
(symbol-function (find-symbol (string-upcase "dot")))
For find-symbol one might need to specify also the package, if there is a special package where the symbol is in.
Converting a list to a vector then is the next building block.
So you need to convert the arguments for your function to vectors (probably first converting them to lists as I showed above). Then you need to find the function (see above). If you have then the function and the arguments, then you can call the function using FUNCALL or APPLY (whatever is more convenient).
The question is a bit unclear, but as far as I understand it you want, when given the list ("1 1 1" "dot" "2 2 2") as input to evaluate the expression (dot "1 1 1" "2 2 2"). In that case you can do this:
(defun apply-infix (arg1 f arg2)
(apply (intern (string-upcase f)) (list arg1 arg2)))
(defun apply-list-infix (lst)
(apply 'apply-infix lst))
(apply-list-infix '("1 1 1" "dot" "2 2 2"))
funcall does not accept a string as a function designator. You need to give it a symbol instead. What you probably want to do is:
Convert the string to upper case (Lisp symbols are usually upper case, and even though it may look like Lisp is case-insensitive, that's just because the reader upcases all symbols it reads by default) (string-upcase).
Create or find a symbol with the given name (intern). Note that, if *package* is not set according to the package your function's name lives in, you need to supply the package name as the second argument to intern.
For instance (for a function named dot in package cl-user:
(funcall (intern (string-upcase "dot") 'cl-user) ...)

Resources