Lời khuyên cho việc chơi golf ở Haskell


73

Bạn có lời khuyên chung nào cho việc chơi golf ở Haskell? Tôi đang tìm kiếm những ý tưởng có thể được áp dụng cho các vấn đề về golf nói chung ít nhất là phần nào cụ thể đối với Haskell. Xin vui lòng chỉ gửi một mẹo cho mỗi câu trả lời.


Nếu bạn chưa quen với việc chơi golf ở Haskell, vui lòng xem Hướng dẫn về Quy tắc chơi gôn ở Haskell . Ngoài ra còn có một phòng trò chuyện Haskell dành riêng: Of Monads and Men .


1
Nhìn thấy số lượng câu trả lời cho đến bây giờ, tôi nghi ngờ về việc liệu Haskell thậm chí có phải là một ngôn ngữ tốt để chơi golf hay không?
Animesh 'the CODER'

10
Tại sao chỉ có một mẹo cho mỗi câu trả lời? Ngoài ra, mỗi ngôn ngữ là một ngôn ngữ tốt để chơi golf. Chỉ cần không luôn luôn mong đợi để giành chiến thắng.
khai mạc

6
@unclemeat Bằng cách này, mọi người có thể nâng cao những người tốt lên hàng đầu mà không nâng cao những người xấu chỉ vì họ được viết bởi cùng một người trong cùng một câu trả lời.
MasterMastic

3
Yêu cầu đặc biệt, nén chuỗi.
J Atkin

Điều này có lẽ không phù hợp như một anwer, nhưng tôi vẫn muốn thêm nó ở đây: wiki.haskell.org/Prime_numbers_misiverse#One-liners
flawr

Câu trả lời:


45

Xác định toán tử infix thay vì hàm nhị phân

Điều này thường tiết kiệm một hoặc hai khoảng trống cho mỗi định nghĩa hoặc cuộc gọi.

0!(y:_)=y
x!(y:z)=(x-1)!z

so với

f 0(y:_)=y
f x(y:z)=f(x-1)z

Các biểu tượng có sẵn cho các nhà khai thác 1 byte là !, #, %, &, và ?. Tất cả các dấu câu ASCII khác hoặc đã được xác định là toán tử bởi Prelude (chẳng hạn như $) hoặc có ý nghĩa đặc biệt trong cú pháp của Haskell (chẳng hạn như @).

Nếu bạn cần nhiều hơn năm toán tử, bạn có thể sử dụng các kết hợp của các toán tử ở trên, chẳng hạn như !#hoặc một số ký tự dấu chấm Unicode nhất định, chẳng hạn như các toán tử này (tất cả 2 byte trong UTF-8):

¡ ¢ £ ¤ ¥ ¦ § ¨ © ¬ ® ¯ ° ± ´ ¶ · ¸ ¿ × ÷

11
Lưu ý: điều này cũng có thể được sử dụng cho các hàm có ba hoặc nhiều đối số. (x!y)z=x+y*z(x#y)z u=x*z+y*ucả hai đều làm việc như mong đợi.
Zgarb

3
Điều này cũng có thể được sử dụng cho các đối số chức năng, ví dụ \f g(!)x y->f g!x ythay vì\f g j x y->j(f g)(x y)
Esolanging Fruit

2
Đôi khi, có ích khi thay đổi các hàm unary thành toán tử nhị phân nếu bạn phải sử dụng dấu ngoặc đơn - g x=…;g(f x)dài hơn_?x=…;0!f x
Angs

29

Sử dụng ký hiệu vô nghĩa (hoặc miễn phí) khi thích hợp

Thường thì một hàm có một hoặc hai tham số có thể được ghi điểm miễn phí.

Vì vậy, việc tìm kiếm một danh sách các bộ dữ liệu có các phần tử được hoán đổi được viết một cách ngây thơ là:

revlookup :: Eq b => b -> [(a, b)] -> Maybe a
revlookup e l=lookup e(map swap l)

(loại này chỉ để giúp bạn hiểu những gì nó đang làm.)

Đối với mục đích của chúng tôi, điều này tốt hơn nhiều:

revlookup=(.map swap).lookup

28

Sử dụng danh sách đơn nguyên

Đánh giá nhanh:

xs >> ys        =  concat $ replicate (length xs) ys
xs >>= f        =  concatMap f xs
mapM id[a,b,c]  =  cartesian product of lists: a × b × c
mapM f[a,b,c]   =  cartesian product of lists: f a × f b × f c

Ví dụ:

  • Lặp lại một danh sách hai lần

    Prelude> "aa">>[1..5]
    [1,2,3,4,5,1,2,3,4,5]
    
  • Ngắn hơn concatMap

    Prelude> reverse=<<["Abc","Defgh","Ijkl"]
    "cbAhgfeDlkjI"
    
  • concatDanh sách ngắn hơn + hiểu

    Prelude> do x<-[1..5];[1..x]
    [1,1,2,1,2,3,1,2,3,4,1,2,3,4,5]
    
  • Sản phẩm của Cartesian

    Prelude> mapM id["Hh","io",".!"]
    ["Hi.","Hi!","Ho.","Ho!","hi.","hi!","ho.","ho!"]
    
  • Danh sách tọa độ trên một mạng

    Prelude> mapM(\x->[0..x])[3,2]
    [[0,0],[0,1],[0,2],[1,0],[1,1],[1,2],[2,0],[2,1],[2,2],[3,0],[3,1],[3,2]]
    

1
Sử dụng lâu hơn tôi thấy hữu ích là [0..b]>>[a]thay vì replicate a b.
Phù thủy lúa mì

3
@WheatWizard a<$[1..b]thậm chí còn ngắn hơn replicate.
Lynn

Sử dụng =<<lực lượng để bạn nhập khẩu Control.Monad. Nếu bạn không cần điều đó vì một số lý do khác, việc hoán đổi các đối số và sử dụng >>=có vẻ ngắn gọn hơn.
dfeuer

OTOH, nếu bạn Data.Traversablevẫn cần , ví dụ về sản phẩm của Cartesian có thể được rút ngắn thành for["Hh","io",".!"]id.
dfeuer

2
(=<<)là trong Prelude , thực sự! Tôi đã sử dụng nó rất nhiều.
Lynn

28

Sử dụng bảo vệ không có điều kiện:

f a=if a>0 then 3 else 7
g a|a>0=3|True=7

Sử dụng dấu chấm phẩy không thụt lề

f a=do
  this
  that
g a=do this;that

Sử dụng các biểu thức boolean cho các hàm boolean

f a=if zzz then True else f yyy
g a=zzz||f yyy

(SO là một nỗi đau về việc để tôi đăng những cái này một cách riêng biệt)


2
Ngoài ra, sử dụng nhiều vệ sĩ thay vì &&khi trong danh sách hiểu.
John Dvorak

Chào ngày 1 tháng 1 - bạn nên đưa ra câu trả lời, tôi sẽ bỏ phiếu cho nó
bazzargh

5
Ví dụ đầu tiên có thể được rút ngắn hơn nữa bởi True=>1>0
John Dvorak

1
trong ví dụ đầu tiên, tôi giả sử bạn có nghĩa làf a=if a>0 then 3 else 7
Cyoce

Bảo vệ thậm chí hoạt động nếu không có đối số trong đó.
Akangka

24

interact :: (String → String) → IO ()

Mọi người thường quên rằng hàm này tồn tại - nó lấy tất cả stdin và áp dụng nó cho hàm (thuần). Tôi thường thấy main-code dọc theo dòng

main=getContents>>=print.foo

trong khi

main=interact$show.foo

là khá ngắn hơn một chút. Nó ở trong khúc dạo đầu nên không cần nhập hàng!


24

Sử dụng GHC 7.10

Phiên bản đầu tiên của GHC có chứa nội dung này được phát hành vào ngày 27 tháng 3 năm 2015 .

Đây là phiên bản mới nhất và Prelude có một số bổ sung mới hữu ích cho việc chơi gôn:

Các (<$>)(<*>)khai thác

Các toán tử hữu ích từ Data.Applicativelàm cho nó trong! <$>chỉ là fmap, vì vậy bạn có thể thay thế map f xfmap f xvới f<$>xmọi nơi và giành lại byte. Ngoài ra, <*>là hữu ích trong Applicativeví dụ cho danh sách:

Prelude> (,)<$>[1..2]<*>"abcd"
[(1,'a'),(1,'b'),(1,'c'),(1,'d'),(2,'a'),(2,'b'),(2,'c'),(2,'d')]

Nhà (<$)điều hành

x<$atương đương với fmap (const x) a; tức là thay thế mọi phần tử trong một container bằng x.

Đây thường là một thay thế tốt đẹp để replicate: 4<$[1..n]ngắn hơn replicate n 4.

Đề xuất có thể gập lại / chuyển đổi

Các chức năng sau đây đã được nâng từ làm việc trên danh sách [a]thành các Foldableloại chung t a:

fold*, null, length, elem, maximum, minimum, sum, product
and, or, any, all, concat, concatMap

Điều này có nghĩa là bây giờ họ cũng làm việc Maybe a, nơi họ hành xử giống như "danh sách có nhiều nhất một yếu tố". Ví dụ null Nothing == True, hoặc sum (Just 3) == 3. Tương tự, lengthtrả về 0 cho Nothingvà 1 cho Justcác giá trị. Thay vì viết x==Just ybạn có thể viết elem y x.

Bạn cũng có thể áp dụng chúng trên các bộ dữ liệu, hoạt động như thể bạn gọi \(a, b) -> [b]trước. Nó gần như hoàn toàn vô dụng, nhưng or :: (a, Bool) -> Boolngắn hơn một ký tự sndelem bngắn hơn (==b).snd.

Các hàm Monoid memptymappend

Không thường xuyên là một trình bảo vệ cuộc sống, nhưng nếu bạn có thể suy ra kiểu đó, memptythì ngắn hơn một byte Nothing, vì vậy có điều đó.


5
+1 Thật tuyệt khi nghe về '<$> `và <*>biến nó thành khúc dạo đầu! Điều đó sẽ hữu ích ngay cả khi Nó không phải là mã golf (ứng dụng là một từ dài như vậy).
ankh-morpork

Cảnh báo về thay thế căn hộ: nếu phiên bản ngôn ngữ của bạn mới hơn thử thách, giải pháp của bạn không đủ điều kiện để giành chiến thắng. Nếu bạn muốn cập nhật câu trả lời hoặc câu trả lời hiện có của mình, đừng ghi đè lên giải pháp hiện tại của bạn. Viết một phụ lục.
John Dvorak

4
Hài hước [1..2]ở đó. đó chỉ là[1,2]
tự hào

2
Trong cùng một phiên bản chúng tôi cũng nhận được <*từ Applicative, mà cho các danh sách là xs <* ys == concatMap (replicate (length ys)) xs. Điều này khác với xs >> yshoặc xs *> ysconcat (replicate (length ys)) xs. puređó là một ngắn hơn returnđến tại thời điểm này quá.
Angs

2
Bây giờ bạn có thể sử dụng <>thay vì mappend, bây giờ (với GHC 8.4.1) là một phần của Prelude.
ბიმო

22

Sử dụng 1<2thay vì True1>2thay vì False.

g x|x<10=10|True=x
f x|x<10=10|1<2=x

3
Điều này không thực sự cụ thể đối với Haskell: nó có thể áp dụng cho mọi ngôn ngữ có loại Boolean trái ngược với các giá trị trung thực và giả của các loại khác.
Peter Taylor

Bất cứ ai có thể giải thích điều này?
MasterMastic

2
Đây không phải là một ví dụ tốt, tôi sẽ chỉ chơi golf như thế này f=max 10.
tự hào

@MasterMastic đây chỉ là viết bằng if(true)các ngôn ngữ khác. trong khúc dạo đầu, nếu không thì thực sự là giá trị boolean True.
tự hào

2
@PeterTaylor Tôi nghĩ rằng điều này vẫn còn có giá trị đối với những người haskellian mới (như tôi) khi tôi lần đầu tiên sử dụng otherwise.
flawr

20

Sử dụng hiểu danh sách (theo cách thông minh)

Mọi người đều biết chúng là cú pháp hữu ích, thường ngắn hơn map+ lambda:

Prelude> [[1..x]>>show x|x<-[1..9]]
["1","22","333","4444","55555","666666","7777777","88888888","999999999"]

Hoặc filter(và tùy chọn a mapcùng một lúc):

Prelude> [show x|x<-[1..60],mod 60x<1]
["1","2","3","4","5","6","10","12","15","20","30","60"]

Nhưng có một số cách sử dụng kỳ lạ có ích ngay bây giờ và sau đó. Đối với một, một sự hiểu biết danh sách không cần phải chứa bất kỳ <-mũi tên nào cả:

Prelude> [1|False]
[]
Prelude> [1|True]
[1]

Có nghĩa là thay vì if p then[x]else[], bạn có thể viết [x|p]. Ngoài ra, để đếm số lượng phần tử của danh sách thỏa mãn điều kiện, bằng trực giác bạn sẽ viết:

length$filter p x

Nhưng cái này ngắn hơn:

sum[1|y<-x,p y]

Tôi thực sự đã sử dụng tất cả những thứ này trước đây, nhưng tôi không nghĩ sẽ đặt chúng ở đây.
tự hào

18

Biết bạn Prelude

Bật GHCi và cuộn qua tài liệu Prelude . Bất cứ khi nào bạn vượt qua một chức năng có một tên ngắn, nó có thể trả tiền để tìm kiếm một số trường hợp có thể hữu ích.

Ví dụ: giả sử bạn muốn chuyển đổi một chuỗi s = "abc\ndef\nghi"thành một chuỗi được phân tách bằng dấu cách , "abc def ghi". Cách rõ ràng là:

unwords$lines s

Nhưng bạn có thể làm tốt hơn nếu bạn lạm dụng maxvà thực tế là \n < space < printable ASCII:

max ' '<$>s

Một ví dụ khác là lex :: String -> [(String, String)], một thứ khá bí ẩn:

Prelude> lex "   some string of Haskell tokens  123  "
[("some"," string of Haskell tokens  123  ")]

Cố gắng fst=<<lex slấy mã thông báo đầu tiên từ một chuỗi, bỏ qua khoảng trắng. Đây là một giải pháp thông minh của henkma sử dụng lex.showtrên Rationalcác giá trị.


16

Khớp một giá trị không đổi

Một sự hiểu biết danh sách có thể mô hình phù hợp trên một hằng số.


[0|0<-l]

Điều này trích xuất 0 của danh sách l, nghĩa là tạo một danh sách có nhiều 0 như trong l.


[1|[]<-f<$>l] 

Điều này làm cho một danh sách như nhiều 1's như có những yếu tố của lfmất vào danh sách rỗng (sử dụng <$>như ghi vào map). Áp dụng sumđể đếm các yếu tố này.

So sánh:

[1|[]<-f<$>l]
[1|x<-l,f x==[]]

[x|(0,x)<-l]

Một hằng số có thể được sử dụng như là một phần của khớp mẫu. Điều này trích xuất các mục thứ hai của tất cả các bộ có mục nhập đầu tiên 0.


Lưu ý rằng tất cả những điều này đòi hỏi một hằng số thực sự, không phải là giá trị của một biến. Ví dụ, let x=1 in [1|x<-[1,2,3]]sẽ xuất ra [1,1,1], không [1], vì xràng buộc bên ngoài bị ghi đè.


14

Sử dụng wordsthay vì một danh sách dài các chuỗi. Điều này không thực sự cụ thể đối với Haskell, các ngôn ngữ khác cũng có thủ thuật tương tự.

["foo","bar"]
words"foo bar"  -- 1 byte longer
["foo","bar","baz"]
words"foo bar baz"  -- 1 byte shorter
["foo","bar","baz","qux"]
words"foo bar baz qux"    -- 3 bytes shorter

14

Biết các chức năng đơn nguyên của bạn

1)
mô phỏng các chức năng đơn âm bằng cách sử dụng mapM.

rất nhiều lần mã sẽ có sequence(map f xs), nhưng nó có thể được thay thế bằng mapM f xs. ngay cả khi chỉ sử dụng sequencemột mình nó là lâu hơn mapM id.

2)
kết hợp các chức năng bằng cách sử dụng (>>=)(hoặc (=<<))

phiên bản đơn nguyên của hàm (>>=)được định nghĩa như vậy:

(f >>= g) x = g (f x) x 

nó có thể hữu ích để tạo các hàm không thể biểu thị như một đường ống dẫn. ví dụ, \x->x==nub xdài hơn nub>>=(==)\t->zip(tail t)tdài hơn tail>>=zip.


+1 - Tôi thậm chí không nhận ra rằng có một ví dụ đơn chức cho các hàm. điều đó có thể rất tiện dụng :)
Jules

2
Lưu ý bên lề: Mặc dù đó là một phần Applicativevà không phải Monadlà việc thực hiện purecũng ngắn hơn constvà thực sự đã giúp tôi trước đây.
ბიმო

14

Đối số có thể ngắn hơn định nghĩa

Tôi vừa bị ruồng bỏ một cách rất tò mò bởi henkma .

Nếu một hàm phụ trợ ftrong câu trả lời của bạn sử dụng toán tử không được sử dụng ở nơi khác trong câu trả lời của bạn và fđược gọi một lần, hãy biến toán tử thành đối số f.

Điều này:

(!)=take
f a=5!a++3!a
reverse.f

Là hai byte dài hơn thế này:

f(!)a=5!a++3!a
reverse.f take

12

Sử dụng toán tử khuyết điểm (:)

khi nối các danh sách, nếu đầu tiên có độ dài 1 thì sử dụng :thay thế.

a++" "++b
a++' ':b  -- one character shorter

[3]++l
3:l    -- three characters shorter

4
Đáng lưu ý rằng đó là kết hợp đúng, vì vậy bạn có thể tiếp tục sử dụng nó cho bất kỳ số lượng các mục duy nhất ở đầu danh sách, ví dụ như 1:2:3:xthay vì [1,2,3]++x.
Jules

11

Đừng sử dụng backticks quá thường xuyên. Backticks là một công cụ tuyệt vời để tạo các phần của các chức năng tiền tố, nhưng đôi khi có thể bị sử dụng sai.

Khi tôi thấy ai đó viết phần phụ này:

(x`v`)

Mặc dù nó giống như chỉ v x.

Một ví dụ khác là viết (x+1)`div`y trái ngược với div(x+1)y.

Tôi thấy nó xảy ra xung quanh divelemthường xuyên hơn bởi vì các hàm này thường được sử dụng như là phần tử trong mã thông thường.


Bạn không có nghĩa là làm cho các phần của chức năng tiền tố ?
Cyoce

@Cyoce Vâng, tất nhiên
tự hào

11

Sử dụng mô hình bảo vệ

Chúng ngắn hơn một lethoặc lambda giải mã các đối số của hàm bạn đang xác định. Điều này giúp khi bạn cần một cái gì đó như fromJusttừ Data.Maybe:

f x=let Just c=… in c

dài hơn

f x=(\(Just c)->c)$…

dài hơn

m(Just c)=c;f x=m$…

dài hơn

f x|Just c<-…=c

Trên thực tế, chúng ngắn hơn ngay cả khi ràng buộc một giá trị cũ đơn giản thay vì giải cấu trúc: xem mẹo của xnor .


Chà, người lambda không cần ký hiệu đô la, và dường như sự thay đổi này làm cho độ dài của điều này và đoạn trích tiếp theo giống nhau
tự hào

1
Tôi giả sử ekhông thực sự là một mã thông báo mà là một biểu thức dài hơn cần $trước nó, thường là như vậy.
Lynn

11

Điều kiện ngắn hơn

last$x:[y|b]

tương đương với

if b then y else x

Đây là cách nó hoạt động:

             [y|b]   x:[y|b]   last$x:[y|b]
if...      +--------------------------------
b == False | []      [x]       x
b == True  | [y]     [x,y]     y

Có nên if b then y else xkhông?
Akangka

@ChristianIrwan Bắt tốt, vâng.
xnor

boolSẽ không sử dụng ngắn hơn vì bạn không cần hiểu danh sách
Potato44

@ Potato44 Đó là trong Data.Bool, chi phí byte để nhập.
xnor

11

Làm việc với dấu trừ

Dấu trừ -là một ngoại lệ gây khó chịu cho nhiều quy tắc cú pháp. Mẹo này liệt kê một số cách ngắn để thể hiện phủ định và phép trừ trong Haskell. Xin vui lòng cho tôi biết nếu tôi đã bỏ lỡ một cái gì đó.

Phủ định

  • Để phủ nhận một biểu thức e, chỉ cần làm -e. Ví dụ, -length[1,2]cho -2.
  • Nếu ethậm chí phức tạp vừa phải, bạn sẽ cần dấu ngoặc đơn xung quanh e, nhưng bạn thường có thể lưu một byte bằng cách di chuyển chúng xung quanh: -length(take 3 x)ngắn hơn -(length$take 3 x).
  • Nếu eđược đi trước bởi =hoặc một toán tử infix có độ cố định nhỏ hơn 6, bạn cần một khoảng trắng: f= -2xác định fk< -2kiểm tra nếu knhỏ hơn -2. Nếu tính cố định là 6 hoặc cao hơn, bạn cần dấu ngoặc: 2^^(-2)cung cấp cho 0.25. Bạn thường có thể sắp xếp lại các thứ để loại bỏ những thứ này: ví dụ, làm -k>2thay vì k< -2.
  • Tương tự, nếu !là một toán tử, thì -a!bđược phân tích cú pháp như (-a)!bthể độ cố định của !nhiều nhất là 6 (so -1<1give True) và -(a!b)ngược lại (so -[1,2]!!0give -1). Độ cố định mặc định của các toán tử do người dùng định nghĩa và các hàm được sao lưu là 9, vì vậy chúng tuân theo quy tắc thứ hai.
  • Để biến phủ định thành một hàm (để sử dụng với mapvv), hãy sử dụng phần (0-).

Phép trừ

  • Để có được một hàm trừ k, sử dụng phần (-k+), thêm -k. kthậm chí có thể là một biểu thức khá phức tạp: (-2*length x+)hoạt động như mong đợi.
  • Để trừ 1, predthay vào đó , sử dụng , trừ khi nó sẽ cần một khoảng trắng ở cả hai bên. Điều này là hiếm và thường xảy ra với untilhoặc một chức năng do người dùng xác định, vì map pred xcó thể được thay thế bởi pred<$>xiterate pred xbởi [x,x-1..]. Và nếu bạn có f pred xmột nơi nào đó, có lẽ bạn nên định nghĩa flà một hàm infix. Xem mẹo này .

11

Hãy thử sắp xếp lại các định nghĩa hàm và / hoặc đối số

Đôi khi bạn có thể lưu một vài byte bằng cách thay đổi thứ tự các trường hợp khớp mẫu trong định nghĩa hàm. Những khoản tiết kiệm này có giá rẻ, nhưng dễ bỏ qua.

Ví dụ, xem xét phiên bản trước đây của (một phần) câu trả lời này :

(g?x)[]=x
(g?x)(a:b)=g(g?x$b)a

Đây là một định nghĩa đệ quy ?, với trường hợp cơ sở là danh sách trống. Vì []không phải là một giá trị hữu ích, chúng ta nên trao đổi các định nghĩa và thay thế nó bằng ký tự đại diện _hoặc đối số giả y, lưu một byte:

(g?x)(a:b)=g(g?x$b)a
(g?x)y=x

Từ cùng một câu trả lời, hãy xem xét định nghĩa này:

f#[]=[]
f#(a:b)=f a:f#b

Danh sách trống xảy ra trong giá trị trả về, vì vậy chúng ta có thể lưu hai byte bằng cách hoán đổi các trường hợp:

f#(a:b)=f a:f#b
f#x=x

Ngoài ra, thứ tự của các đối số hàm đôi khi có thể tạo sự khác biệt bằng cách cho phép bạn loại bỏ khoảng trắng không cần thiết. Hãy xem xét một phiên bản trước của câu trả lời này :

h p q a|a>z=0:h p(q+2)(a-1%q)|1<2=1:h(p+2)q(a+1%p)

Có một khoảng trắng khó chịu giữa hptrong nhánh đầu tiên. Chúng ta có thể thoát khỏi nó bằng cách định nghĩa h a p qthay vì h p q a:

h a p q|a>z=0:h(a-1%q)p(q+2)|1<2=1:h(a+1%p)(p+2)q

10

Đừng lãng phí bảo vệ "nếu không"

Một người bảo vệ cuối cùng là một người nắm bắt tất cả True(ngắn hơn 1>0) có thể được sử dụng để liên kết một biến. So sánh:

... |1>0=1/(x+y)
... |z<-x+y=1/z

... |1>0=sum l-sum m
... |s<-sum=s l-s m

Vì người bảo vệ là bắt buộc và bị lãng phí, nên rất ít cần thiết để làm cho nó có giá trị. Nó đủ để lưu một cặp parens hoặc liên kết một biểu thức có độ dài 3 được sử dụng hai lần. Đôi khi bạn có thể phủ nhận các vệ sĩ để làm cho trường hợp cuối cùng là biểu thức sử dụng tốt nhất một ràng buộc.


10

Sử dụng ,thay vì &&trong bảo vệ

Nhiều điều kiện trong một bảo vệ mà tất cả phải giữ có thể được kết hợp với ,thay vì &&.

f a b | a/=5 && b/=7 = ...
f a b | a/=5 ,  b/=7 = ...

2
Nó không phải là điều kiện hoặc là, bạn có thể làm những việc như thế này:f xs m | [x] <- xs, Just y <- m, x > 3 = y
chim bông lau

10

Cú pháp ngắn hơn cho khai báo cục bộ

Đôi khi bạn cần xác định một hàm hoặc toán tử cục bộ, nhưng nó tốn rất nhiều byte để ghi wherehoặc let…innâng nó lên mức cao nhất bằng cách thêm các đối số phụ.

g~(a:b)=2!g b where k!l=k:take(a-1)l++(k+1)!drop(a-1)l
g~(a:b)=let k!l=k:take(a-1)l++(k+1)!drop(a-1)l in 2!g b
g~(a:b)=2!g b$a;(k!l)a=k:take(a-1)l++((k+1)!drop(a-1)l)a

May mắn thay, Haskell có một cú pháp khó hiểu và hiếm khi được sử dụng nhưng khá hợp lý cho các khai báo cục bộ :

fun1 pattern1 | let fun2 pattern2 = expr2 = expr1

Trong trường hợp này:

g~(a:b)|let k!l=k:take(a-1)l++(k+1)!drop(a-1)l=2!g b

Bạn có thể sử dụng cú pháp này với khai báo nhiều câu lệnh hoặc nhiều khai báo và thậm chí nó còn lồng nhau:

fun1 pattern1 | let fun2 pattern2 = expr2; fun2 pattern2' = expr2' = expr1
fun1 pattern1 | let fun2 pattern2 = expr2; fun3 pattern3 = expr3 = expr1
fun1 pattern1 | let fun2 pattern2 | let fun3 pattern3 = expr3 = expr2 = expr1

Nó cũng hoạt động cho các biến ràng buộc hoặc các mẫu khác, mặc dù các bộ bảo vệ mẫu có xu hướng ngắn hơn cho điều đó trừ khi bạn cũng có các hàm ràng buộc.


3
Điều này cũng hoạt động bên trong danh sách hiểu : [f 1|let f x=x+1].
Laikoni

10

Tránh repeat n

-- 8 bytes, whitespace might be needed before and after
repeat n

-- 8 bytes, whitespace might be needed before
cycle[n]

-- 7 bytes, whitespace might be needed before and after, can be reused,
-- needs an assignment, n needs to be global
l=n:l;l

-- 7 bytes, never needs whitespace, n needs to derive from Enum,
-- n has to be short enough to be repeated twice
[n,n..]

Một trong bốn biểu thức đó sẽ tạo ra một danh sách vô hạn ncác.

Đây là một mẹo rất cụ thể nhưng nó có thể tiết kiệm tới 3 byte!


4
Nếu nlà toàn cục, l=n:l;lcó cùng độ dài và hoạt động cho (một số) biểu thức dài hơn. (Có thể cần khoảng trắng mặc dù.)
Ørjan Johansen

@ RjanJohansen Tôi kết hợp nó vào bài viết. Cảm ơn!
con người

10

Điều kiện ngắn hơn khi một kết quả là danh sách trống

Khi bạn cần một điều kiện trả về danh sách Ahoặc danh sách trống []tùy thuộc vào một số điều kiện C, thì tồn tại một số lựa chọn thay thế ngắn hơn cho các cấu trúc điều kiện thông thường:

if(C)then(A)else[] -- the normal conditional
last$[]:[A|C]      -- the golfy all-round-conditional
concat[A|C]        -- shorter and works when surrounded by infix operator
id=<<[A|C]         -- even shorter but might conflict with other infix operators
[x|C,x<-A]         -- same length and no-conflict-guarantee™
[0|C]>>A           -- shortest way, but needs surrounding parenthesis more often than not

Ví dụ: 1 , 2


Cái thứ hai có A[]chuyển đổi.
Ørjan Johansen

@ RjanJohansen Đã sửa, cảm ơn!
Laikoni

1
Aha! Nhưng *>có độ cố định cao hơn >>(vẫn hơi thấp cho sự thoải mái.)
Ørjan Johansen

9

Quy tắc phân tích cú pháp Lambda

Một biểu thức lambda không thực sự cần dấu ngoặc đơn xung quanh nó - nó chỉ tham lam nắm lấy mọi thứ để toàn bộ mọi thứ vẫn phân tích cú pháp, ví dụ cho đến khi

  • một paren kết thúc - (foo$ \x -> succ x)
  • một trong - let a = \x -> succ x in a 4
  • Cuối con đường - main = getContents>>= \x -> head $ words x
  • Vân vân..

gặp phải, và có một số trường hợp cạnh kỳ lạ trong đó điều này có thể giúp bạn tiết kiệm một hoặc hai byte. Tôi tin rằng \cũng có thể được sử dụng để xác định các toán tử, vì vậy khi khai thác điều này, bạn sẽ cần một khoảng trắng khi viết lambda trực tiếp sau một toán tử (như trong ví dụ thứ ba).

Dưới đây là một ví dụ về việc sử dụng lambda là điều ngắn nhất tôi có thể tìm ra. Mã về cơ bản trông giống như:

a%f=...
f t=sortBy(% \c->...)['A'..'Z']

9

Thay thế letbằng lambda

Điều này thường có thể rút ngắn một định nghĩa phụ trợ đơn độc không thể bị ràng buộc với một người bảo vệ hoặc được xác định trên toàn cầu vì một số lý do. Ví dụ: thay thế

let c=foo a in bar

ngắn hơn 3 byte

(\c->bar)$foo a

Đối với nhiều định nghĩa phụ trợ, mức tăng có thể nhỏ hơn, tùy thuộc vào số lượng định nghĩa.

let{c=foo a;n=bar a}in baz
(\c n->baz)(foo a)$bar a

let{c=foo a;n=bar a;m=baz a}in qux
(\c n m->qux)(foo a)(bar a)$baz a

let{c=foo a;n=bar a;m=baz a;l=qux a}in quux
(\c n m l->quux)(foo a)(bar a)(baz a)$qux a

Nếu một số định nghĩa đề cập đến các định nghĩa khác, việc lưu byte theo cách này thậm chí còn khó hơn:

let{c=foo a;n=bar c}in baz
(\c->(\n->baz)$bar c)$foo a

letNhắc nhở chính với điều này là cho phép bạn xác định các biến đa hình, nhưng lambdas thì không, như được lưu ý bởi @ChristianSievers. Ví dụ,

let f=length in(f["True"],f[True])

kết quả (1,1), nhưng

(\f->(f["True"],f[True]))length

đưa ra một lỗi loại.


1
Nó hiếm khi quan trọng, nhưng "tương đương về mặt ngữ nghĩa" hứa hẹn một chút quá nhiều. Chúng tôi có polymorpic let, vì vậy chúng tôi có thể làm let f=id in (f 0,f True). Nếu chúng ta cố gắng viết lại cái này với lambda thì nó không gõ.
Christian Sievers

@ChristianSievers Điều đó đúng, cảm ơn bạn đã lưu ý. Tôi đã chỉnh sửa nó.
Zgarb

8

Bind sử dụng bảo vệ

Khi xác định hàm được đặt tên, bạn có thể liên kết một biểu thức với một biến trong bộ bảo vệ. Ví dụ,

f s|w<-words s=...

không giống như

f s=let w=words s in ...
f s=(\w->...)$words s

Sử dụng điều này để lưu vào các biểu thức lặp đi lặp lại. Khi biểu thức được sử dụng hai lần, nó sẽ hòa vốn ở độ dài 6, mặc dù các vấn đề về khoảng cách và quyền ưu tiên có thể thay đổi điều đó.

(Trong ví dụ, nếu biến ban đầu skhông được sử dụng, thì nó sẽ ngắn hơn để làm

g w=...
f=g.words

nhưng điều đó không đúng khi ràng buộc các biểu thức phức tạp hơn.)


Đây không phải là một trường hợp trùng lặp / đặc biệt của câu trả lời này sao?
Lynn

@Lynn Nhìn lại, đó là một trường hợp đặc biệt, nhưng khi tôi đọc câu trả lời đó, Juství dụ này khiến tôi nghĩ rằng nó phù hợp với mô hình để trích xuất từ ​​một container, thay vì lưu trữ trên một biểu thức.
xnor

8

Sử dụng (0<$)thay vì lengthđể so sánh

Khi kiểm tra nếu một danh sách adài hơn một danh sách b, người ta thường viết

length a>length b

Tuy nhiên, thay thế từng yếu tố của cả hai danh sách bằng cùng một giá trị, ví dụ 0, và sau đó so sánh hai danh sách đó có thể ngắn hơn:

(0<$a)>(0<$b)

Hãy thử trực tuyến!

Ngoặc là cần thiết bởi vì <$và các toán tử so sánh ( ==, >, <=, ...) có mức độ ưu tiên tương tự 4, mặc dù trong một số trường hợp khác mà họ có thể không cần thiết, tiết kiệm nhiều hơn byte.


8

Ngắn hơn transpose

Để sử dụng transposechức năng Data.Listphải được nhập khẩu. Nếu đây là chức năng duy nhất cần nhập, người ta có thể lưu một byte bằng foldrđịnh nghĩa sau transpose:

import Data.List;transpose
e=[]:e;foldr(zipWith(:))e

Lưu ý rằng hành vi chỉ giống hệt nhau cho một danh sách các danh sách có cùng độ dài.

Tôi đã sử dụng thành công ở đây .


8

Nhận hậu tố

Sử dụng scanr(:)[]để có được các hậu tố của một danh sách:

λ scanr(:)[] "abc"
["abc","bc","c",""]

Điều này là ngắn hơn nhiều so với tailssau import Data.List. Bạn có thể làm tiền tố với scanr(\_->init)=<<id(được tìm thấy bởi Ørjan Johansen).

λ  scanr(\_->init)=<<id $ "abc"
["","a","ab","abc"]

Điều này tiết kiệm một byte hơn

scanl(\s c->s++[c])[]

Có lẽ scanl(flip(:))[] "abc"= ["","a","ba","cba"]cũng đáng được đề cập - đôi khi các tiền tố bị ngược lại không thành vấn đề.
Lynn

3
Rjan Johansen đã tìm thấy một thay thế ngắn hơn một byte cho tiền tố:scanr(\_->init)=<<id
Laikoni
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.