2010-01-19 34 views
18

Đang cố gắng tìm hiểu Haskell. Tôi đang cố gắng để viết một chức năng đơn giản để loại bỏ một số từ một danh sách mà không cần sử dụng chức năng được xây dựng trong (xóa ... tôi nghĩ). Để đơn giản, hãy giả sử tham số đầu vào là một số nguyên và danh sách là một danh sách Integer. Đây là mã tôi có, hãy cho tôi biết những gì xảy ra với đoạn mã sauTìm hiểu Haskell: Cách xóa một mục khỏi Danh sách trong Haskell

areTheySame :: Int -> Int-> [Int] 

areTheySame x y | x == y = [] 
       | otherwise = [y] 

removeItem :: Int -> [Int] -> [Int] 

removeItem x (y:ys) = areTheySame x y : removeItem x ys 
+3

Cũng giống như một mẹo, nứt mở báo cáo Haskell 98 và đọc mã được cung cấp cho các chức năng Prelude là một mắt mở thực sự. Bạn sẽ học được rất nhiều điều về cách mọi thứ hoạt động (và học được rất nhiều thứ về những điều bạn không nên bận tâm, ngoại trừ một bài tập học trí tuệ!). Đây là chuyến tham quan phong nha của nó: http://ww2.cs.mu.oz.au/172/Haskell/tourofprelude.html. http://members.chello.nl/hjgtuyl/tourdemonad.html và http://cs.anu.edu.au/student/comp1100/haskell/tourofsyntax.html cũng hữu ích. (Đáng buồn haskell.org vẫn còn xuống vì vậy tôi không thể kết nối bạn trực tiếp với báo cáo.) –

Trả lời

27

Các vấn đề khác là vấn đề là nhà điều hành :. Tuy nhiên, tôi cho rằng hàm areTheySame trả về danh sách của bạn là phương pháp sai. Thay vì chuyển sang các nhà điều hành ++, một thực hiện tốt hơn chức năng đó sẽ là:

removeItem _ []     = [] 
removeItem x (y:ys) | x == y = removeItem x ys 
        | otherwise = y : removeItem x ys 

Như bạn có thể thấy, đây là một thực hiện khá đơn giản. Ngoài ra, consing như thế này là ít hơn nhiều thuế cho chương trình của bạn hơn là nối một loạt các danh sách với nhau. Nó cũng có những lợi ích khác, chẳng hạn như làm việc lười biếng.

10

Nhà điều hành : không làm những gì bạn nghĩ nó:

(:) :: a -> [a] -> [a] 

Phải mất một mục kiểu a và thêm nó vào đầu danh sách loại a. Bạn đang sử dụng nó để tham gia hai danh sách loại a. Để làm được điều đó, bạn cần sử dụng ++:

(++) :: [a] -> [a] -> [a] 

Ngoài ra, nếu bạn thực hiện chức năng đệ quy, nó cần điều kiện kết thúc. Vì vậy, hãy thử điều này:

removeItem _ [] = [] 
removeItem x (y:ys) = areTheySame x y ++ removeItem x ys 

Bằng cách đó, khi bạn đến cuối danh sách, chức năng sẽ ngừng đệ quy.

+0

Nếu bạn thực sự áp dụng lời giải thích cho mã của mình và thay đổi ':' cho '++' thì mã của bạn sẽ đúng , hiện tại nó đang tạo danh sách các danh sách ('areTheySame' luôn trả về một danh sách và bạn thêm nó vào đầu danh sách bằng cách sử dụng': '). – ahans

+0

Nếu bạn giữ định nghĩa kiểu gốc, bạn sẽ gặp phải lỗi kiểu. – ahans

+0

Bạn nói đúng - tôi đã sao chép dòng của anh ấy và quên thay đổi phần tôi vừa nói về việc thay đổi. Dù bằng cách nào, nó có lẽ không phải là cách tốt nhất để làm mọi thứ, nhưng nó chắc chắn hoạt động. –

3

Đây là sửa chữa tối thiểu để làm ví dụ công việc của bạn:

removeItem :: Int -> [Int] -> [Int] 
removeItem _ []  = [] 
removeItem x (y:ys) = areTheySame x y ++ removeItem x ys 

Trước tiên, bạn cần phải sử dụng ++ để nối danh sách, như các nhà điều hành : sử dụng bởi bạn cho biết thêm chỉ là một yếu tố để khởi đầu của một danh sách (nó không thể được sử dụng để thêm danh sách với một phần tử cũng như không thêm danh sách trống). Trước tiên, bạn so sánh đầu danh sách (y) với mục bạn muốn xóa và trả lại chính xác mục hoặc danh sách trống bằng cách sử dụng areTheySame. Sau đó, bạn muốn đệ quy tiếp tục sử dụng removeItem trên phần còn lại của danh sách (ys). Danh sách kết quả cần được ghép nối bằng cách sử dụng ++.

Thứ hai, như Chris Lutz đã lưu ý, bạn cần điều kiện kết thúc khi bạn đến cuối danh sách. Bằng cách thêm dòng này, Haskell biết phải làm gì với một danh sách trống (có nghĩa là, không có gì, chỉ cần trả về một danh sách trống). Như Chuck đã nói, bạn có thể đơn giản hóa mã cho nhiệm vụ này bằng cách removeItem không ủy nhiệm nhiệm vụ so sánh, nhưng so sánh chính nó và vứt bỏ phần tử nếu nó bị xóa, nếu không hãy giữ nó ở đầu danh sách (sử dụng :). Trong mọi trường hợp, tiếp tục đệ quy với phần còn lại của danh sách.

-- nothing can be removed from an empty list 
-- ==> return empty list and stop recursion 
removeItem _ []  = [] 

-- if the list is not empty, cut off the head in y and keep the rest in ys 
--     if x==y, remove y and continue 
removeItem x (y:ys) | x == y = removeItem x ys  
--     otherwise, add y back and continue 
        | otherwise = y : removeItem x ys 
3

Để tham khảo, bạn có thể quan tâm đến việc xem how it's done in delete from Data.List.

Bạn có thể rời khỏi areTheySame như là, nhưng sau đó bạn sẽ cần phải sử dụng concatMap trong removeItem sụp đổ danh sách rỗng:

removeItem :: Int -> [Int] -> [Int] 
removeItem x xs = concatMap (areTheySame x) xs 

hoặc tương đương

removeItem :: Int -> [Int] -> [Int] 
removeItem x = concatMap (areTheySame x) 

Lưu ý rằng các loại của bạn chức năng có thể tổng quát hơn:

areTheySame :: (Eq a) => a -> a -> [a] 
removeItem :: (Eq a) => a -> [a] -> [a] 

Điều này cho phép xóa các mục khỏi danh sách thuộc bất kỳ loại nào mà == được xác định, không chỉ là Int.

7

Bạn cũng có thể làm điều này như một danh sách-hiểu

delete :: Eq a => a -> [a] -> [a] 
delete deleted xs = [ x | x <- xs, x /= deleted ] 
+0

mặc dù điều này có thể chậm hơn một chút – Jay

0

Tôi tin rằng tất cả các giải pháp cho công việc cho đến nay khác so với Data.List.delete, mà chỉ xóa các thành viên đầu tiên.

deleteFromList x xs = 
    case break (==x) xs of 
    (_,[]) -> xs 
    (notsat,sat) -> notsat ++ tail sat 

là nỗ lực của tôi để chỉ xóa thành viên đầu tiên (chưa đạt điểm DL).

Không rõ hành vi mà người đăng hàng đầu muốn.

1

Tôi đã viết một chức năng chỉ trong một dòng mã:

remove element list = filter (\e -> e/=element) list 

Ví dụ:

remove 5 [1..10] 

[1,2,3,4,6,7,8,9,10 ]

remove 'b' ['a'..'f'] 

"acdef"