Exercise 2-4
January 20, 2017
Scheme makes is possible to write both versions of squeeze in a single function:
(define (squeeze str c-or-s)
(let ((dels (if (char? c-or-s)
(list c-or-s)
(string->list c-or-s))))
(list->string
(filter (lambda (c) (not (member c dels)))
(string->list str)))))
The second argument may be either a character or a string; the let builds a list of characters to be deleted. Then the target string is converted to a list of characters, each is filtered according to the list of characters to be deleted, and the remaining characters are converted back to a string. The for is typical of C, the higher-order function filter is typical of Scheme. I’m biased, but I think the Scheme version is simpler because it doesn’t require the auxiliary variables i and j, which can both be a source of error. Here’s the function in action, both ways:
> (squeeze "Programming Praxis" #\P) "rogramming raxis" > (squeeze "Programming Praxis" "aeiou") "Prgrmmng Prxs"
You can run the program at http://ideone.com/pCg02E.
As a perl user I would do this a perl way…
sub squeeze { my $r = quotemeta $_[1]; return my $y = $_[0] =~ s/[$r]//gr } print squeeze(@ARGV),"\n";Perl golf would get it down to
sub squeeze{return$_[0]=~s/[@{[quotemeta$_[1]]}]//gr}Here’s some C++:
#include <stdio.h> template <typename T, typename PRED> void filter(T *p, PRED pred) { for (T *q = p; *q != T(0); q++) { if (!pred(*q)) *p++ = *q; } *p = 0; } template <typename T> bool member(T a, T *p) { for ( ; *p != T(0); p++) { if (a == *p) return true; } return false; } void squeeze(char *s, char *t) { filter(s,[=](char c) { return member(c,t); }); } int main(int argc, char *argv[]) { squeeze(argv[1],argv[2]); printf("%s\n", argv[1]); }Or some more traditional C:
#include <stdio.h> #include <stdbool.h> void f(char *s, const char *t) { int i = 0, j = 0, k = 0; while (true) { if (t[i] == 0) { i = 0; s[k++] = s[j++]; } else if (s[j] == 0) { s[k++] = 0; break; } else if (t[i] == s[j]) { i = 0; j++; } else { i++; } } } int main(int argc, char *argv[]) { f(argv[1],argv[2]); printf("%s\n", argv[1]); return 0; }Guile:
(define (squeeze str chars) (string-delete (string->char-set chars) str))
Python strings have translation-table methods.
>>> x = str.maketrans('','','aeiouyäö') >>> 'Pidä pää kylmänä ja lopeta twiittailu'.translate(x) 'Pd p klmn j lpt twttl'Here’s a Haskell version. It also works on any list whose elements support equality testing.
@Globules: I was going to suggest
import Control.Monad squeeze :: (Eq a, MonadPlus m) => [a] -> m a -> m a squeeze t = join . fmap nullset where nullset c | c `elem` t = mzero | otherwise = return c main = print $ squeeze "ace" "abcde"but really that is just your solution with mfilter instead of filter.
@matthew Your MonadPlus suggestion made me wonder what was the “most general” type I could give it. But then I got lost in a maze of twisty little typeclasses (Foldable, Alternative, etc.) and now I will just go to bed… :-)