Điểm của 'const' trong Haskell Prelude là gì?


92

Nhìn qua Haskell Prelude, tôi thấy một chức năng const :

const x _ = x

Tôi dường như không thể tìm thấy bất cứ điều gì liên quan đến chức năng này.

Vấn đề ở đây là gì? Bất cứ ai có thể cho một ví dụ về nơi chức năng này có thể được sử dụng?


10
Một ví dụ: backgroundColor :: Text -> Colorđối với tôibackgroundColor = const White
Zhen

Câu trả lời:


83

Nó hữu ích để chuyển đến các hàm bậc cao hơn khi bạn không cần tất cả sự linh hoạt của chúng. Ví dụ: toán tử trình tự đơn nguyên >>có thể được định nghĩa theo toán tử liên kết đơn nguyên là

x >> y = x >>= const y

Nó có phần gọn gàng hơn so với sử dụng lambda

x >> y = x >>= \_ -> y

và bạn thậm chí có thể sử dụng miễn phí

(>>) = (. const) . (>>=)

mặc dù tôi không đặc biệt khuyên bạn điều đó trong trường hợp này.


9
+1. Nó cũng xuất hiện thường xuyên khi sử dụng các tổ hợp phân tích cú pháp.
Fred Foo

47
Ahh vì vậy nó giống như một 'trình tạo hàm' - tôi sử dụng nó với một đối số và nó cung cấp cho tôi một hàm (nhận một đối số) luôn trả về một giá trị không đổi. Vì vậy, map (const 42) [1..5]kết quả là [42, 42, 42, 42, 42].
thợ rèn

2
stusmith: Bạn hiểu rồi. constrất hữu ích cho việc áp dụng cho một đối số duy nhất để mang lại một hàm khi cần một hàm (chẳng hạn như chuyển tới map).
Conal

8
@stusmith: Bạn có thể sử dụng nó theo một số cách thú vị:head = foldr const (error "Prelude.head: empty list")
raectors

27

Để thêm vào câu trả lời trực tiếp tuyệt vời của hammar: các hàm khiêm tốn như constidthực sự hữu ích như một hàm bậc cao hơn vì cùng một lý do là chúng là cơ bản trong phép tính tổ hợp SKI .

Tôi không nghĩ rằng các chức năng dạo đầu của haskell được mô hình hóa một cách có ý thức theo hệ thống chính thức đó hay bất cứ thứ gì. Chỉ là việc tạo ra những thứ trừu tượng phong phú trong haskell là rất dễ dàng, vì vậy bạn thường thấy những thứ lý thuyết này trở nên hữu ích trong thực tế.

Trình cắm không biết xấu hổ, nhưng tôi đã viết blog về cách phiên bản Ứng dụng (->)thực sự là SKtổ hợp ở đây , nếu đó là loại điều bạn yêu thích.


8
Chà, các tổ hợp SKI chắc chắn đã ảnh hưởng đến Prelude. Tôi nhớ đã tranh luận với Joe Fasel xem liệu tổ hợp S có nên được đưa vào hay không.
augustss

4
Ngẫu nhiên, ((->) e)cũng là đơn nguyên của người đọc - với Readervà tương tự chỉ là newtypetrình bao bọc - và asksau đó là chức năng id, vì vậy đó cũng là trình Itổ hợp. Nếu bạn nhìn thay vì tại cơ sở BCKW gốc Haskell Curry của, B, K, và Wfmap, returnjointương ứng.
CA McCann

1
Liên kết blog trong câu trả lời đã chết. Bây giờ nó sẽ trỏ ở đây: brandon.si/code/…
Tiếp theo:

22

Một ví dụ đơn giản để sử dụng constData.Functor.(<$). Với chức năng này, bạn có thể nói: Tôi có ở đây một cái functor với thứ gì đó nhàm chán trong đó, nhưng thay vào đó tôi muốn có thứ thú vị khác trong đó mà không làm thay đổi hình dạng của cái functor. Ví dụ

import Data.Functor

42 <$ Just "boring"
--> Just 42

42 <$ Nothing
--> Nothing

"cool" <$ ["nonsense","stupid","uninteresting"]
--> ["cool","cool","cool"]

Định nghĩa là:

(<$) :: a -> f b -> f a
(<$) =  fmap . const

hoặc viết không phải là vô nghĩa:

cool <$ uncool =  fmap (const cool) uncool

Bạn xem cách constđược sử dụng ở đây để "quên" đầu vào.


21

Tôi dường như không thể tìm thấy bất cứ điều gì liên quan đến chức năng này.

Nhiều câu trả lời khác thảo luận về các ứng dụng tương đối bí truyền (ít nhất là đối với người mới) của const. Đây là một đơn giản: bạn có thể sử dụngconst để loại bỏ lambda có hai đối số, loại bỏ đối số đầu tiên nhưng làm điều gì đó thú vị với đối số thứ hai.

Ví dụ, cách triển khai (không hiệu quả nhưng mang tính hướng dẫn) sau đây length,

length' = foldr (\_ acc -> 1 + acc) 0

có thể được viết lại thành

length' = foldr (const (1+)) 0

mà có lẽ là thanh lịch hơn.

Biểu thức const (1+)thực sự tương đương về mặt ngữ nghĩa \_ acc -> 1 + acc, bởi vì nó lấy một đối số, ném nó đi và trả về phần (1+) .


4
Tôi đã mất 5 phút để hiểu cách làm việc này :)
Mukesh Soni

15

Một cách sử dụng khác là triển khai các hàm thành viên lớp có đối số giả không được đánh giá (được sử dụng để giải quyết các kiểu không rõ ràng). Ví dụ có thể có trong Data.bits:

instance Bits Int where
  isSigned = const True
  bitSize  = const wordSize
  ...

Bằng cách sử dụng const, chúng tôi nói rõ ràng rằng chúng tôi đang xác định các giá trị không đổi.

Cá nhân tôi không thích việc sử dụng các tham số giả, nhưng nếu chúng được sử dụng trong một lớp thì đây là một cách khá hay để viết các phiên bản.


Đối số proxy thực sự tốt hơn nhiều và khi nhắm mục tiêu GHC gần đây, các ứng dụng loại thực hiện thủ thuật một cách gọn gàng.
dfeuer

2

constcó thể chỉ là phần triển khai bạn đang tìm kiếm kết hợp với các chức năng khác. Đây là một ví dụ tôi đã phát hiện ra.

Giả sử chúng ta muốn viết lại một cấu trúc gồm 2 bộ dữ liệu thành một cấu trúc 2 bộ dữ liệu khác. Tôi có thể diễn đạt điều này như vậy:

((a,b),(c,d))  (a,(c,(5,a)))

Tôi có thể đưa ra một định nghĩa rõ ràng về đối sánh mẫu:

f ((a,b),(c,d)) = (a,(c,(5,a)))

Điều gì sẽ xảy ra nếu tôi muốn một giải pháp vô nghĩa (ngầm) cho những loại viết lại này? Một số suy nghĩ và tìm kiếm sau đó, câu trả lời là chúng ta có thể diễn đạt bất kỳ đoạn viết lại nào (&&&), const, (.), fst, snd. Lưu ý rằng (&&&)từControl.Arrow .

Giải pháp của ví dụ sử dụng các hàm này là:

(fst.fst &&& (fst.snd &&& (const 5 &&& fst.fst)))

Lưu ý sự tương tự với (a,(c,(5,a))). Nếu chúng ta thay thế &&&bằng ,? Sau đó, nó đọc:

(fst.fst, (fst.snd, (const 5, fst.fst)))

Chú ý aphần tử đầu tiên của phần tử đầu tiên như thế nào , và đó là những gì fst.fstdự án. Chú ý cphần tử đầu tiên của phần tử thứ hai như thế nào và đó là những gì fst.snddự án. Đó là, các biến trở thành đường dẫn đến nguồn của chúng.

constcho phép chúng tôi giới thiệu các hằng số. Thật thú vị làm thế nào mà cái tên đi kèm với ý nghĩa!

Sau đó tôi khái quát hóa ý tưởng này với applicative để bạn có thể viết bất kỳ chức năng trong một phong cách vô nghĩa (vì vậy miễn là bạn có phân tích trường hợp có sẵn như là chức năng, chẳng hạn như maybe, either, bool). Một lần nữa, constđóng vai trò giới thiệu các hằng số. Bạn có thể thấy công việc này trong Data.Function.Tacit gói .

Khi bạn bắt đầu một cách trừu tượng, ở mục tiêu, và sau đó hướng tới việc thực hiện, bạn có thể ngạc nhiên trước câu trả lời. Có nghĩa là, bất kỳ một chức năng nào cũng có thể bí ẩn như bất kỳ một bánh răng nào trong máy. Tuy nhiên, nếu bạn lùi lại để đưa toàn bộ máy vào tầm nhìn, bạn có thể hiểu được bối cảnh mà bánh răng đó là cần thiết.


2

Giả sử bạn muốn tạo một danh sách Nothingsbằng độ dài của một chuỗi. Khi consttrả về đối số đầu tiên của nó, bất kể đối số thứ hai, bạn có thể làm:

listOfNothings :: String -> [Maybe Char]
listOfNothings = (map . const) Nothing

hoặc, rõ ràng hơn:

listOfNothing st = map (const Nothing) st

0

Giả sử bạn muốn xoay một danh sách. Đây là một cách thành ngữ để làm như vậy trong Haskell:

rotate :: Int -> [a] -> [a] rotate _ [] = [] rotate n xs = zipWith const (drop n (cycle xs)) xs

Hàm này nén hai mảng với hàm const, mảng đầu tiên là mảng tuần hoàn vô hạn, mảng thứ hai là mảng bạn đã bắt đầu.

const hoạt động như kiểm tra giới hạn và sử dụng mảng ban đầu để kết thúc mảng tuần hoàn.

Xem: Xoay danh sách trong Haskell


0

Tôi dường như không thể tìm thấy bất cứ điều gì liên quan đến chức năng này.

Giả sử bạn muốn tạo tất cả các dãy con của một danh sách nhất định.

Đối với mỗi phần tử danh sách, tại một thời điểm nhất định, bạn có lựa chọn Đúng (bao gồm nó trong dãy con hiện tại) hoặc Sai (không bao gồm nó). Điều này có thể được thực hiện bằng cách sử dụng chức năng filterM .

Như thế này:

 λ> import Control.Monad
 λ> :t filterM
 filterM :: Applicative m => (a -> m Bool) -> [a] -> m [a]
 λ> 

Ví dụ, chúng tôi muốn tất cả các dãy con của [1..4].

 λ> filterM  (const [True, False])  [1..4]
 [[1,2,3,4],[1,2,3],[1,2,4],[1,2],[1,3,4],[1,3],[1,4],[1],[2,3,4],[2,3],[2,4],[2],[3,4],[3],[4],[]]
 λ> 
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.