Bu kelimeleri uygulamak katlamadan sonra bir işlem sonrası adım olmadan mümkün müdür?


14

Real World Haskell, bölüm 4, sayfa 98,words kıvrımlar kullanılarak uygulanıp uygulanamayacağını soruyor ve bu da benim sorum:

Mümkün mü? Değilse, neden? Eğer öyleyse, nasıl?

Ben her boşluk olmayan çıktı listesindeki son sözcüğe (bu otherwisenöbetçi olur ) başlaması gerektiğini ve bir boşluk emtpy kelimesinin eklenmesini tetiklemek gerekir fikrine dayanan aşağıdaki ile geldi zaten bir tane yoksa çıkış listesi (bu if- then- içinde ele alınır else).

myWords :: String -> [String]
myWords = foldr step [[]]
  where
    step x yss@(y:ys)
      | x == ' ' = if y == "" then yss else "":yss
      | otherwise = (x:y):ys

Açıkça bu çözüm yanlıştır, çünkü giriş dizesindeki önde gelen boşluklar, dizelerin çıktı listesinde bir önde gelen boş dizeyle sonuçlanır.

Yukarıdaki bağlantıda, diğer okuyucular için önerilen çözümlerin birkaçına baktım ve birçoğu benim çözümümle benzer şekilde çalışıyor, ancak genellikle katlamanın çıktısını "post-process", örneğin taileğer varsa boş, önde gelen bir dizedir.

Diğer yaklaşımlarda tuples (aslında sadece çiftler) kullanılır, böylece kat çifti ile ilgilenir ve önde gelen / sondaki boşlukları iyi idare edebilir.

Tüm bu yaklaşımlarda foldr(veya başka bir kat, fwiw) kutudan nihai çıktıyı sağlayan işlev değildir; her zaman çıktıyı bir şekilde ayarlamanız gereken başka bir şey vardır.

Bu nedenle, ilk soruya geri dönüyorum ve wordskıvrımlar kullanarak (izleyen / öndeki / tekrarlanan boşlukları doğru şekilde işleyecek şekilde) gerçekten mümkün olup olmadığını soruyorum . Tarafından kat kullanan bir katlama işlevi dış fonksiyonu olmak zorunda olduğu anlamına:

myWords :: String -> [String]
myWords input = foldr step seed input

Yanıtlar:


13

Doğru anlarsam, gereksinimleriniz şunları içerir:

(1) words "a b c" == words " a b c" == ["a", "b", "c"]
(2) words "xa b c" == ["xa", "b", "c"] /= ["x", "a", "b", "c"] == words "x a b c"

Bu, sahip olamayacağımız anlamına gelir

words = foldr step base

herhangi stepve base.

Gerçekten, eğer sahip olsaydık, o zaman

words "xa b c"
= def words and foldr
step 'x' (words "a b c")
= (1)
step 'x' (words " a b c")
= def words and foldr
words "x a b c"

ve bu çelişkilidir (2).

Sonra kesinlikle bazı post-processing gerekir foldr.


1
Bu dili daha çok seviyorum ...
Enrico Maria De Angelis

Hatta ["xa"] == words "xa" == step 'x' (words "a") == step 'x' (words " a") == words "x a" == ["x", "a"]katlama yönlerinin her ikisi için de geçerli bir argüman olma avantajına sahip olan
Cireo

5

@chi'nin words"a" kıvrımı kullanarak uygulayamayacağınız harika bir argümanı vardır , ancak kıvrım s'yi kullanarak söylediniz .

words = filterNull . words1
    where
    filterNull = foldr (\xs -> if null xs then id else (xs:)) []
    words1 = foldr (\c -> if c == ' ' then ([]:) else consHead c) []
    consHead c []       = [[c]]
    consHead c (xs:xss) = (c:xs):xss

Hem en dıştaki hem de en içteki fonksiyon katlanır. ;-)


Ne demek istediğimi biliyorsun, ama seçici olmak için +1: P
Enrico Maria De Angelis

1

Evet. Biraz zor foldrolsa da, CPS'de ( Devam Tarzı ) devam ederseniz , tek bir şey ve başka bir şey kullanarak bu işi düzgün bir şekilde yapabilirsiniz . Daha önce özel bir tür chunksOffonksiyon göstermiştim .

Bu tür kıvrımlarda akümülatörümüz, dolayısıyla kıvrımın sonucu bir işlevdir ve bunu nihai bir sonuç elde etmek için bir kimlik türüne uygulamak zorundayız. Bu nedenle, burada tek bir kat kullandığımız ve bunun türü işlevi içerdiğinden, bu son bir işleme aşaması olarak sayılabilir veya sayılmaz. Tartışmaya aç :)

ws :: String -> [String]
ws str = foldr go sf str $ ""
         where
         sf :: String -> [String]
         sf s = if s == " " then [""] else [s]
         go :: Char -> (String -> [String]) -> (String -> [String])
         go c f = \pc -> let (s:ss) = f [c]
                         in case pc of
                            ""        -> dropWhile (== "") (s:ss)
                            otherwise -> case (pc == " ", s == "") of
                                         (True, False)  -> "":s:ss
                                         (True, True)   -> s:ss
                                         otherwise      -> (pc++s):ss

λ> ws "   a  b    c   "
["a","b","c"]

sf : Başlamak için başlangıç ​​fonksiyon değeri.

go : Yineleyici işlevi

Aslında CPS'in gücünü tam olarak kullanmıyoruz, çünkü her bir önceki karaktere pcve akım karakterine sahibiz c. Bu çok yararlı chunksOfbir parçalama sırasında yukarıda sözü edilen işlev [Int]içine [[Int]]elemanlarının artan bir dizi kırık her zaman.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.