Lời khuyên cho việc chơi golf trong sed


19

Bạn có lời khuyên chung nào cho việc chơi golf trong sed? Tôi đang tìm kiếm những ý tưởng có thể áp dụng cho các vấn đề về golf-code và ít nhất cũng hơi cụ thể đối với sed (ví dụ: "xóa bình luận" không phải là một câu trả lời).

Xin vui lòng gửi một lời khuyên cho mỗi câu trả lời.


4
Không thực sự là một mẹo chơi gôn (nhưng vẫn là một mẹo để chơi gôn): các nguồn cấp dữ liệu tiêu thụ nhiều byte như dấu chấm phẩy, vì vậy bạn có thể giữ mã của mình ngắn dễ đọc.
Dennis

Không phải là một mẹo, mà là một vấn đề: Tôi có GNU sed, nhưng Flệnh không bao giờ hoạt động. Có ai biết tại sao không?
seshoumara

@seshoumara Fhoạt động trên GNU sed của tôi (thử nghiệm Debian). Nó chỉ in -nếu đọc từ stdin, tất nhiên, nhưng đó là mong đợi. Bạn nhận được sed -e 'F;Q' /etc/hostnamegì từ ?
Toby Speight

@TobySpeight Điều đó gây ra lỗi này : char 1: unknown command: F. Tôi phải cập nhật sed có lẽ; bạn có phiên bản nào Các Llệnh cũng không hoạt động, nhưng nó nào vô dụng từ năm -l ntồn tại. Mọi thứ khác được đề cập trên trang web của GNU sed đều hoạt động.
seshoumara

1
Tôi mở phòng chat bash, sed and dccho tất cả những ai muốn nói chuyện và hỏi về những ngôn ngữ này. Hãy tạo nên một cộng đồng!
seshoumara

Câu trả lời:


11

Nếu bạn cần sử dụng nhãn thì chắc chắn bạn sẽ muốn tên nhãn của mình càng ngắn càng tốt. Trong thực tế được đưa đến mức cực đoan, bạn thậm chí có thể sử dụng chuỗi rỗng làm tên nhãn:

:    # define label ""
p    # print pattern space
b    # infinite loop! - branch to label ""

4
Kể từ gnu sed 4.3, hành vi này đã bị xóa . :bây giờ yêu cầu một nhãn hiệu.
Kevin

Thật vậy, đây cũng là liên kết cam kết git thực tế . Tôi đoán đối với PPCG điều này sẽ không thay đổi nhiều, vì chúng tôi được phép đăng câu trả lời cho GNU sed 4.2.x, nhưng thật đáng mừng, dù đáng tiếc, thủ thuật này sẽ không chính thức hoạt động nữa.
seshoumara

8

Các tài liệu GNU sed mô tả các slệnh như "Swiss Army Knife sed của" . Nhưng nếu tất cả những gì bạn muốn làm là thay thế tất cả các phiên bản của một ký tự bằng một ký tự khác, thì ylệnh là thứ bạn cần:

y/a/b/

là một char ngắn hơn:

s/a/b/g

nó cũng nhanh hơn và có thể trao đổi ký tự tại chỗ:y/12/21/
mikeerv

6

Xem xét sử dụng cú pháp regex mở rộng (trong GNU sed). Các -rtùy chọn chi phí một byte trong ghi bàn, nhưng sử dụng nó chỉ một lần để loại bỏ các dấu xồ nguợc từ một cặp \(...\)đã trả cho chính nó.


2
Với ghi chú bổ sung -rdường như là sedcụ thể của GNU .
manatwork

@manat - đã thêm (nhưng đó là câu trả lời của Wiki cộng đồng, vì vậy bạn có thể tự chỉnh sửa).
Toby Speight

Tất nhiên. Tôi chỉ không coi đó là một phần của tiền boa, chỉ là một ghi chú bổ sung.
manatwork

Và nó tiếp tục trả tiền cho bản thân khi sử dụng +, ?, {}|trong các trận đấu regex, vì không có dấu xồ nguợc cần một trong hai.
seshoumara

-Ehoạt động như một bí danh -rtrong nhiều sedtriển khai nếu tôi nhớ chính xác.
phk

6

Khi liên tục thay thế trong một vòng lặp:

loop:
s/foo/bar/g
tloop

thường không cần thiết để thay thế trên toàn cầu, vì vòng lặp cuối cùng sẽ thay thế tất cả các lần xuất hiện:

# GNU sed
:
s/foo/bar/
t

Cũng lưu ý phần mở rộng GNU ở trên: một nhãn có thể có một tên trống, tiết kiệm nhiều byte quý giá hơn. Trong các triển khai khác, nhãn không thể để trống và nhảy mà không có nhãn chuyển luồng đến cuối tập lệnh (nghĩa là giống như n).


1
Tên nhãn trống là dành riêng cho GNU, POSIX yêu cầu các nhánh không có đối số để nhảy đến cuối tập lệnh (dường như là hành vi trong BSD và Busybox, cũng trong GNU sed nếu bạn không thêm trống :)
ninjalj

2
Nhãn không tên luôn là một lỗi trong GNU sed, không phải là phần mở rộng và trong phiên bản 4.3 trở lên, lỗi này đã được sửa chữa một cách đáng tiếc. Xem tại đây .
seshoumara

5

Không có số học tích hợp, nhưng các phép tính có thể được thực hiện bằng số thập phân hoặc số thập phân đơn nhất. Đoạn mã sau chuyển đổi thập phân thành UCD, với x là đơn vị và 0 là dấu phân cách chữ số:

s/[1-9]/0&/g
s/[5-9]/4&/g
y/8/4/
s/9/4&/g
s/4/22/g
s/[37]/2x/g
s/[26]/xx/g
s/[1-9]/x/g

và đây là chuyển đổi trở lại thập phân:

s/0x/-x/g
s/xx/2/g
y/x/1/
s/22/4/g
s/44/8/g
s/81/9/g
s/42/6/g
s/21/3/g
s/61/7/g
s/41/5/g
s/-//g

Cả hai đều được lấy từ một câu trả lời cho "Nhân hai số mà không sử dụng bất kỳ số nào" .

Unary old unary có thể được chuyển đổi bằng cặp vòng lặp này từ câu trả lời này thành "{Số xoăn};" , đơn vị ở đâu ;. Tôi đã sử dụng vxđể phù hợp với Roman cho 510; bxuất phát từ "bis".

# unary to decimal
:d
/;/{
s/;;;;;/v/g
s/vv/x/g
/[;v]/!s/x\+/&0/
s/;;/b/g
s/bb/4/
s/b;/3/
s/v;/6/
s/vb/7/
s/v3/8/
s/v4/9/
y/;bvx/125;/
td
}

# Decimal to unary
:u
s/\b9/;8/
s/\b8/;7/
s/\b7/;6/
s/\b6/;5/
s/\b5/;4/
s/\b4/;3/
s/\b3/;2/
s/\b2/;1/
s/\b1/;0/
s/\b0//
/[^;]/s/;/&&&&&&&&&&/g
tu

1
... Và nếu bạn phải sử dụng một trong hai thứ này, bạn gần như chắc chắn đã mất mã golf, mặc dù bạn vẫn có thể cạnh tranh với các câu trả lời Java ;-) Mặc dù vậy vẫn rất vui khi sử dụng.
Chấn thương kỹ thuật số

Việc chuyển đổi từ unary đơn giản sang thập phân cho câu trả lời sai cho đầu vào unary tương đương với dạng thập phân X0X, ví dụ 108. Dòng chịu trách nhiệm cho điều này là /[;v]/!s/\b/0/2, cần phải thay đổi để /[;v]/!s:x\+:&0:nó hoạt động. Xem tại đây .
seshoumara

@seshoumara, liên kết của bạn dường như là một trang trống. Nhưng điều hoàn toàn hợp lý là tôi đã mắc lỗi khi trích xuất mã đó từ câu trả lời được tham chiếu, vì vậy tôi sẽ chỉ áp dụng cách khắc phục của bạn.
Toby Speight

Liên kết tải chính xác, nhưng tôi đã mong đợi một cái gì đó ngoài một trang màu xám có "TIO" và một cái gì đó trông giống như logo Ubuntu - đó có phải là ý định không? Và tôi đã đề cập đến câu trả lời thứ hai mà tôi đã tham khảo ( 58007 ), vì đó là nơi mẫu đơn giản bắt nguồn.
Toby Speight

Liên kết TIO phải chứa mã được sửa, cộng với đầu vào mẫu, 108 ở dạng đơn. Khi chạy mã, bạn sẽ thấy kết quả chính xác 108 chứ không phải 180, như trước đây được tạo bởi dòng mã cố định đó. Cập nhật câu trả lời tham khảo là hoàn toàn tùy thuộc vào bạn. Đây là một wiki cộng đồng.
seshoumara

4

Như đã đề cập trong man sed(GNU), bạn có thể sử dụng bất kỳ ký tự nào làm dấu phân cách cho các biểu thức thông thường bằng cách sử dụng cú pháp

\%regexp%

nơi %giữ chỗ cho bất kỳ nhân vật nào.

Điều này rất hữu ích cho các lệnh như

/^http:\/\//

mà ngắn hơn là

\%^http://%

Những gì được đề cập trong GNU sed thủ công nhưng không vào man sedlà bạn có thể thay đổi delimiters của s///y///là tốt.

Ví dụ: lệnh

ss/ssg

loại bỏ tất cả các dấu gạch chéo từ không gian mô hình.


4

Nếu không bị cấm rõ ràng bởi câu hỏi, sự đồng thuận cho câu hỏi meta này là đầu vào số có thể là đơn nhất. Điều này giúp bạn tiết kiệm 86 byte số thập phân thành đơn nguyên theo câu trả lời này .


Không phải đó là sự đồng thuận meta cho sed đề cập đến định dạng đơn nguyên cũ? Tôi có một số câu trả lời trong đó một đầu vào trong UCD sẽ giúp tôi, trong trường hợp đó là một trong hai cách.
seshoumara

@seshoumara Ý tôi là unary, không phải UCD
Chấn thương kỹ thuật số

Sau đó, việc chuyển đổi từ số thập phân sang đơn vị cũ đơn giản giúp bạn tiết kiệm được 126 byte theo câu trả lời mà bạn đã liên kết. 86 byte dành cho việc chuyển đổi sang UCD.
seshoumara

4

Mở rộng dựa trên câu trả lời về mẹo này , liên quan đến việc chuyển đổi giữa các định dạng số thập phân và số đơn giản, tôi trình bày các phương pháp thay thế sau đây, với những ưu điểm và nhược điểm của chúng.

Số thập phân đến đơn vị đơn giản: 102 + 1 (cờ r) = 103 byte. Tôi tính \tlà một tab theo nghĩa đen, là 1 byte.

h
:
s:\w::2g
y:9876543210:87654321\t :
/ /!s:$:@:
/\s/!t
x;s:-?.::;x
G;s:\s::g
/\w/{s:@:&&&&&&&&&&:g;t}

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

Ưu điểm: nó ngắn hơn 22 byte và là phần bổ sung, nó hoạt động với các số nguyên âm làm đầu vào

Nhược điểm: nó ghi đè lên không gian giữ. Tuy nhiên, vì nhiều khả năng bạn cần phải chuyển đổi số nguyên đầu vào ngay khi bắt đầu chương trình, nên hạn chế này hiếm khi được cảm nhận.

Đồng bằng đơn giản thành thập phân: 102 + 1 (cờ r) = 103 byte

s:-?:&0:
/@/{:
s:\b9+:0&:
s:.9*@:/&:
h;s:.*/::
y:0123456789:1234567890:
x;s:/.*::
G;s:\n::
s:@::
/@/t}

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

Ưu điểm: nó ngắn hơn 14 byte. Lần này cả hai phiên bản tip hoạt động cho số nguyên âm làm đầu vào.

Nhược điểm: nó ghi đè lên không gian giữ

Đối với một thử thách phức tạp, bạn sẽ phải điều chỉnh các đoạn mã này để hoạt động với các thông tin khác có thể tồn tại trong không gian mẫu hoặc không gian giữ, bên cạnh số cần chuyển đổi. Mã có thể được đánh gôn nhiều hơn, nếu bạn biết bạn chỉ làm việc với các số dương hoặc một mình số 0 sẽ không phải là đầu vào / đầu ra hợp lệ.

Một ví dụ về câu trả lời thách thức như vậy, nơi tôi đã tạo và sử dụng các đoạn mã này, là Đối ứng của một số (1 / x) .


Đối với unary-to-binary, bạn có thể lưu hai byte bằng cách kết hợp hai thay thế cuối cùng : s:\n|@$::g. tio.run/##K05N@f@/2ErX3krNwIpL30G/ Kẻ
Jordan

Tôi đã thử riêng của tôi ở bộ chuyển đổi thập phân sang đơn nguyên. Đây là 97 byte :) Hãy thử trực tuyến! (cũng không yêu cầu -r, nhưng với sự đồng thuận mới, các lá cờ không được tính vào dù sao, và nó không làm xáo trộn không gian giữ)
Kritixi Lithos

Trên thực tế nếu bạn thay đổi dòng cuối cùng từ /\n/tathành /\n/t, bạn lưu 1 byte để nhận 96
Kritixi Lithos

@Cowsquack Cảm ơn, 96 thật tuyệt! Đừng có thời gian bây giờ, sẽ xem xét nó vào cuối tuần này.
seshoumara

Chắc chắn, hãy gửi cho tôi một ping trên trò chuyện sau đó :)
Kritixi Lithos

3

Chúng ta hãy nói về tTcác lệnh, mặc dù chúng được giải thích trong trang man, thật dễ dàng để quên nó và vô tình giới thiệu các lỗi, đặc biệt là khi mã bị phức tạp.

Tuyên bố trang Man cho t:

Nếu a s///đã thực hiện thay thế thành công kể từ khi dòng đầu vào cuối cùng được đọc và kể từ lệnh t hoặc T cuối cùng, thì phân nhánh thành nhãn.

Ví dụ cho thấy ý tôi muốn nói: giả sử bạn có một danh sách các số và bạn muốn đếm xem có bao nhiêu tiêu cực. Mã một phần dưới đây:

1{x;s/.*/0/;x}                   # initialize the counter to 0 in hold space
s/-/&/                           # check if number is negative
t increment_counter              # if so, jump to 'increment_counter' code block
b                                # else, do nothing (start a next cycle)

:increment_counter
#function code here

Có vẻ ổn, nhưng nó không phải là. Nếu số đầu tiên là số dương, mã đó vẫn sẽ nghĩ đó là số âm, bởi vì bước nhảy được thực hiện tcho dòng đầu tiên đầu tiên được thực hiện bất kể, vì đã có sự sthay thế thành công khi chúng tôi khởi tạo bộ đếm! Đúng là : /-/b increment_counter.

Nếu điều này có vẻ dễ dàng, bạn vẫn có thể bị lừa khi thực hiện nhiều lần nhảy qua lại để mô phỏng các chức năng. Trong ví dụ của chúng tôi, increment_counterkhối mã chắc chắn sẽ sử dụng rất nhiều slệnh. Quay trở lại với b maincó thể làm cho một kiểm tra khác trong "chính" rơi vào cùng một cái bẫy. Đó là lý do tại sao tôi thường trở về từ các khối mã với s/.*/&/;t label. Nó xấu, nhưng hữu ích.


2

Thay vì xóa không gian mẫu bằng s/.*//, hãy sử dụng zlệnh (chữ thường) nếu bạn đi với GNU sed. Bên cạnh số byte thấp hơn, nó có lợi thế là nó sẽ không bắt đầu chu kỳ tiếp theo như lệnh d, điều này có thể hữu ích trong một số tình huống.


1
Cũng có thể có ích nếu bạn có các chuỗi nhiều byte không hợp lệ (không khớp với .).
Toby Speight

2

Tôi biết đây là một luồng cũ, nhưng tôi chỉ tìm thấy các bộ chuyển đổi thập phân UCD vụng về đó, với gần một trăm byte, một số thậm chí còn làm rối không gian giữ hoặc yêu cầu các sedphiên bản bị lỗi đặc biệt .

Đối với thập phân sang UCD tôi sử dụng (68 byte; trước đây được đăng tốt nhất ở đây 87 byte)

s/$/\n9876543210/
:a
s/\([1-9]\)\(.*\n.*\)\1\(.\)/\3x\2\1\3/
ta
P;d

UCD đến thập phân là (cũng là 66 byte; trước đây được đăng tốt nhất ở đây 96)

s/$/\n0123456789/
:a      
s/\([0-8]\)x\(.*\n.*\)\1\(.\)/\3\2\1\3/
ta      
P;d
  • \ntrong thay thế không phải là di động. Bạn có thể sử dụng một ký tự khác thay thế và lưu hai byte, nhưng bạn sẽ cần nhiều byte hơn để xóa phụ lục thay vì P;d; xem nhận xét tiếp theo. Hoặc, nếu không gian giữ của bạn trống, hãy thực hiện G;s/$/9876543210/mà không bị phạt byte.
  • Nếu bạn cần xử lý thêm, bạn sẽ cần thêm một số byte s/\n.*//thay thế P;d.
  • Bạn có thể lưu hai byte cho mỗi sedphiên bản GNU cũ bị lỗi
  • Không, bạn không thể lưu sáu dấu gạch chéo ngược đó vì các biểu thức chính quy mở rộng không thực hiện phản hồi

Không có số thập phân sang UCD và bộ chuyển đổi ngược được đăng trong luồng này làm rối không gian giữ hoặc yêu cầu các phiên bản sed bị lỗi.
seshoumara

Câu trả lời của riêng bạn từ ngày 6 tháng 4 sử dụng không gian vàng và sẽ chỉ chạy với sedcác phiên bản cũ vi phạm tiêu chuẩn POSIX.
Philippos

Tôi không thực hiện chuyển đổi thập phân sang UCD! Đọc lại chủ đề một cách cẩn thận. UCD có nghĩa là 12 được chuyển đổi thành 0x0xx (câu trả lời của bạn tính toán), trong khi unary đơn giản (những gì câu trả lời của tôi tính toán) có nghĩa là 12 được chuyển đổi thành xxxxxxxxxxxx. Tôi chọn @ làm biểu tượng, nhưng bạn hiểu ý. Và hơn nữa, trên PPCG, người ta không cần phải tuân thủ tiêu chuẩn POSIX.
seshoumara

Nếu điều đó làm bạn hài lòng, cảnh sát trưởng
Philippos

2

Đọc toàn bộ đầu vào cùng một lúc với -z

Thường thì bạn cần phải thao tác trên toàn bộ đầu vào cùng một lúc thay vì một dòng tại một thời điểm. Các Nlệnh rất hữu ích cho điều đó:

:
$!{N;b}

... nhưng thông thường bạn có thể bỏ qua nó và sử dụng -zcờ thay thế.

Các -zlá cờ làm NUL sử dụng sed ( \0) như dòng tách đầu vào của nó thay vì \n, vì vậy nếu bạn biết đầu vào của bạn sẽ không chứa \0, nó sẽ đọc tất cả các đầu vào cùng một lúc như là một “dòng” duy nhất:

$ echo 'foo
> bar
> baz' | sed -z '1y/ao/eu/'
fuu
ber
bez

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


2

Nối một dòng mới trong một byte

Các Glệnh gắn thêm một dòng mới và các nội dung của vũ trụ giữ cho không gian mô hình, vì vậy nếu không gian tổ chức của bạn là trống rỗng, thay vì điều này:

s/$/\n/

Bạn có thể làm được việc này:

G

Chuẩn bị một dòng mới trong ba byte

Các Hlệnh gắn thêm một dòng mới và các nội dung của không gian mẫu cho không gian giữ, và xgiao dịch hoán đổi hai, vì vậy nếu không gian tổ chức của bạn là trống rỗng, thay vì điều này:

s/^/\n/

Bạn có thể làm được việc này:

H;x

Điều này sẽ gây ô nhiễm không gian giữ của bạn, vì vậy nó chỉ hoạt động một lần. Tuy nhiên, đối với hai byte nữa, bạn có thể xóa không gian mẫu của mình trước khi hoán đổi, đây vẫn là một khoản tiết kiệm của hai byte:

H;z;x

1

Trong sed, thứ gần nhất với chức năng mà bạn có thể có là nhãn. Một hàm rất hữu ích vì bạn có thể thực thi mã của nó nhiều lần, do đó tiết kiệm được rất nhiều byte. Tuy nhiên, trong sed, bạn sẽ cần chỉ định nhãn trả về và như vậy bạn không thể gọi "hàm" này nhiều lần trong toàn bộ mã theo cách bạn sẽ làm bằng các ngôn ngữ khác.

Cách giải quyết tôi sử dụng là thêm vào một trong hai ký ức một cờ, được sử dụng để chọn nhãn trả về. Điều này hoạt động tốt nhất khi mã chức năng chỉ cần một không gian bộ nhớ duy nhất (cái còn lại).

Ví dụ cho thấy những gì tôi muốn nói: lấy từ một dự án của tôi để viết một trò chơi nhỏ trong sed

# after applying the player's move, I overwrite the pattern space with the flag "P"
s/.*/P/
b check_game_status
:continue_turn_from_player
#code

b calculate_bot_move
:return_bot_move
# here I call the same function 'check_game_status', but with a different flag: "B"
s/.*/B/
b check_game_status
:continue_turn_from_bot
#code (like say 'b update_screen')

:check_game_status   # this needs just the hold space to run
#code
/^P$/b continue_turn_from_player
/^B$/b continue_turn_from_bot

Các nhãn nên được chơi golf tất nhiên chỉ một chữ cái, tôi đã sử dụng tên đầy đủ để giải thích rõ hơn.


1

Regex rỗng tương đương với regex đã gặp trước đó

(cảm ơn Riley vì đã khám phá ra điều này từ một bài nộp anagol )

Dưới đây là một ví dụ trong đó chúng ta có nhiệm vụ tạo 100 @giây trong một bộ đệm trống.

s/$/@@@@@@@@@@/;s/.*/&&&&&&&&&&/ # 31 bytes
s/.*/@@@@@@@@@@/;s//&&&&&&&&&&/  # 30 bytes

Giải pháp thứ hai ngắn hơn 1 byte và sử dụng thực tế là các biểu thức chính trống được điền vào biểu thức chính thức gặp phải cuối cùng. Ở đây, đối với sự thay thế thứ hai, regex cuối cùng là .*, vì vậy regex trống ở đây sẽ được lấp đầy .*. Điều này cũng hoạt động với regexes trong /conditionals/.

Lưu ý rằng đó là regex gặp phải trước đây , vì vậy những điều sau đây cũng sẽ hoạt động.

s/.*/@@@@@@@@@@/;/@*/!s/$/@/;s//&&&&&&&&&&/

Regex trống được lấp đầy @*thay $vì bởi vì s/$/@/không bao giờ đạt được.


Vâng, câu trả lời tốt. Tôi thậm chí đã thực hiện các biểu thức dài hơn để chúng có thể được khớp lại như thế này (do đó làm cho chương trình ngắn hơn).
Toby Speight

0

Chủ yếu là bước vô dụng:

y|A-y|B-z|

Điều này sẽ chỉ dịch Asang Bysang z(... và -sang -;), nhưng không có gì khác, vì vậy

sed -e 'y|A-y|B-z|' <<<'Hello world!'

sẽ trở lại:

Hello world!

Bạn có thể đảm bảo điều này sẽ là vô ích, cho mẫu bằng cách sử dụng này trên các giá trị thập lục phân hợp cụ thể thấp hơn (chỉ chứa 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, ehoặc f.)


2
Đây có phải là thứ bạn phát hiện ra một cách khó khăn không?! ;-)
Toby Speight

Tôi thích các kịch bản vô dụng: sed '; ;/s/b;y|A-y|B-z|;s ;s/ //; ; ;' <<<'Hello world'(Tại sao điều này không triệt tiêu không gian?)
F. Hauri
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.