Là thực hiện chức năng từ này có thể mà không cần một bước hậu xử lý sau khi gấp?


14

Real World Haskell, chương 4, trang 98 của bản in hỏi liệu wordscó thể được thực hiện bằng cách sử dụng các nếp gấp không và đây cũng là câu hỏi của tôi:

Có thể không? Nếu không, tại sao? Nếu là nó, làm thế nào?

Tôi đã đưa ra những điều sau, dựa trên ý tưởng rằng mỗi phi không gian nên được thêm vào từ cuối cùng trong danh sách đầu ra (điều này xảy ra trong phần otherwisebảo vệ) và rằng một khoảng trắng sẽ kích hoạt việc nối thêm một từ emtpy vào danh sách đầu ra nếu chưa có danh sách này (điều này được xử lý trong if- then- else).

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

Rõ ràng giải pháp này là sai, vì các khoảng trắng hàng đầu trong chuỗi đầu vào dẫn đến một chuỗi trống hàng đầu trong danh sách chuỗi đầu ra.

Ở liên kết trên, tôi đã xem xét một số giải pháp được đề xuất cho các độc giả khác và nhiều giải pháp trong số đó hoạt động tương tự như giải pháp của tôi, nhưng họ thường "xử lý hậu kỳ" đầu ra của nếp gấp, ví dụ như bằng cách sử dụng tailnó nếu có là một chuỗi hàng đầu trống.

Các cách tiếp cận khác sử dụng các bộ dữ liệu (thực tế chỉ là các cặp), do đó, giao dịch gấp với cặp và có thể xử lý tốt các khoảng trắng ở đầu / cuối.

Trong tất cả các cách tiếp cận này, foldr(hoặc một nếp gấp khác, fwiw) không phải là chức năng cung cấp đầu ra cuối cùng ra khỏi hộp; luôn luôn có một cái gì đó khác phải điều chỉnh đầu ra bằng cách nào đó.

Do đó, tôi quay trở lại câu hỏi ban đầu và hỏi liệu thực tế có thể thực hiện được không words(theo cách nó xử lý chính xác các dấu cách / hàng đầu / lặp lại) bằng cách sử dụng các nếp gấp. Bằng cách sử dụng các nếp gấp, ý tôi là chức năng gấp phải là chức năng ngoài cùng:

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

Câu trả lời:


13

Nếu tôi hiểu chính xác, yêu cầu của bạn bao gồm

(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"

Điều này ngụ ý rằng chúng ta không thể có

words = foldr step base

cho bất kỳ stepbase.

Thật vậy, nếu chúng ta có điều đó, thì

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"

và điều này mâu thuẫn (2).

Bạn chắc chắn cần một số xử lý hậu kỳ sau foldr.


1
Tôi yêu ngôn ngữ này nhiều hơn ...
Enrico Maria De Angelis

Hoặc thậm chí ["xa"] == words "xa" == step 'x' (words "a") == step 'x' (words " a") == words "x a" == ["x", "a"], có lợi ích là một đối số hợp lệ cho một trong hai hướng gấp
Cireo

5

@chi có một cuộc tranh luận tuyệt vời mà bạn không thể thực hiện wordsbằng "a" gấp, nhưng bạn đã nói sử dụng lần s .

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

Cả hai chức năng ngoài cùng và trong cùng là nếp gấp. ;-)


Tôi nghĩ bạn hiểu ý tôi, nhưng +1 vì kén chọn: P
Enrico Maria De Angelis

1

Đúng. Mặc dù có một chút khó khăn, bạn vẫn có thể thực hiện công việc này đúng cách bằng cách sử dụng một foldrvà không có gì khác nếu bạn tham gia vào CPS ( Phong cách tiếp tục ). Tôi đã cho thấy một loại chunksOfchức năng đặc biệt trước đây.

Trong các loại nếp gấp này, bộ tích lũy của chúng ta, do đó kết quả của nếp gấp là một hàm và chúng ta phải áp dụng nó cho một loại đầu vào nhận dạng để chúng ta có kết quả cuối cùng. Vì vậy, điều này có thể được tính là giai đoạn xử lý cuối cùng hay không vì chúng ta đang sử dụng một lần duy nhất ở đây và loại của nó bao gồm chức năng. Mở để tranh luận :)

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 : Giá trị hàm ban đầu để bắt đầu.

go : Hàm lặp

Chúng tôi thực sự không sử dụng đầy đủ sức mạnh của CPS ở đây vì chúng tôi có cả nhân vật trước pcvà nhân vật hiện ctại trong tay trong mỗi lượt. Nó rất hữu ích trong chunksOfchức năng nêu trên trong khi chunking một [Int]thành [[Int]]mỗi khi một chuỗi tăng dần của các yếu tố đã bị phá vỡ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.