Sứa , 45 byte
p
\Ai
\&
>(&]&|0
<*&d
&~bN
10
( )/+
/*
Hãy thử trực tuyến!
Giải trình
Đây là chương trình phức tạp nhất (và cũng dài nhất) mà tôi đã viết trong Jellyfish cho đến nay. Tôi không biết liệu tôi có thể phá vỡ điều này một cách dễ hiểu hay không, nhưng tôi đoán tôi sẽ phải thử.
Sứa cung cấp một nhà điều hành lặp đi lặp lại khá chung, \
mà sẽ giúp rất nhiều với "tìm kiếm thứ N một cái gì đó ". Một trong những ngữ nghĩa của nó là "lặp một hàm trên một giá trị cho đến khi một hàm kiểm tra riêng biệt mang lại sự thật" (thực tế, hàm kiểm tra nhận được cả phần tử hiện tại và phần tử cuối cùng, nhưng chúng ta sẽ chỉ nhìn vào phần tử hiện tại) . Chúng ta có thể sử dụng điều này để thực hiện chức năng "số hợp lệ tiếp theo". Một tình trạng quá tải khác \
là "lặp một hàm trên giá trị bắt đầu N lần". Chúng ta có thể sử dụng hàm trước đó và lặp lại nó trên 0
N lần, trong đó N là đầu vào. Tất cả điều đó được thiết lập khá chính xác với phần mã này:
p
\Ai
\&
> 0
(Lý do tại sao 0
, đầu vào thực tế cho hàm kết quả, có một chút phức tạp và tôi sẽ không đi sâu vào chúng ở đây.)
Vấn đề với tất cả những điều này là, chúng tôi sẽ không chuyển giá trị hiện tại cho chức năng kiểm tra theo cách thủ công. Nhà \
điều hành sẽ làm điều này cho chúng tôi. Vì vậy, bây giờ chúng ta đã xây dựng một hàm unary duy nhất (thông qua các thành phần, móc, dĩa và cà ri) lấy một số và cho chúng ta biết đó có phải là một số hợp lệ hay không (nghĩa là một số được chia cho tổng số chữ số và sản phẩm chữ số của nó). Điều này khá không tầm thường khi bạn không thể tham khảo đối số. Không bao giờ. Đó là vẻ đẹp này:
(&]&|
<*&d
&~bN
10
( )/+
/*
Đây (
là một hook unary , có nghĩa là nó gọi hàm bên dưới ( f
) trên đầu vào của nó (giá trị hiện tại x
), và sau đó chuyển cả hai chúng sang hàm kiểm tra sang phải ( g
), nghĩa là nó tính toáng(f(x), x)
.
Trong trường hợp của chúng tôi, f(x)
là một hàm tổng hợp khác có được một cặp với sản phẩm chữ số và tổng chữ số của x
. Điều đó có nghĩa là g
sẽ là một hàm có cả ba giá trị để kiểm tra xem x
có hợp lệ không.
Chúng ta sẽ bắt đầu bằng cách xem xét cách f
tính tổng sản phẩm và chữ số. Đây là f
:
&~b
10
( )/*
/+
&
cũng là thành phần (nhưng cách khác vòng). ~
là currying để 10~b
cung cấp cho hàm tính các chữ số thập phân của một số và vì chúng ta chuyển nó sang &
bên phải, đó là điều đầu tiên sẽ xảy ra với đầu vàox
. Phần còn lại sử dụng danh sách các chữ số này để tính tổng và sản phẩm của chúng.
Để tính một tổng, chúng ta có thể gấp bổ sung trên đó, đó là /+
. Tương tự như vậy, để tính toán sản phẩm, chúng tôi nhân bội với nó /*
. Để kết hợp cả hai kết quả này thành một cặp, chúng tôi sử dụng một cặp móc (
và )
. Cấu trúc của nó là:
()g
f
(Nơi f
và g
là sản phẩm và số tiền tương ứng.) Chúng ta hãy thử tìm hiểu tại sao điều này cho chúng ta một cặp f(x)
và g(x)
. Lưu ý rằng hook bên phải )
chỉ có một đối số. Trong trường hợp này, đối số khác được ngụ ý là ;
bao bọc các đối số của nó trong một cặp. Hơn nữa, hook cũng có thể được sử dụng như các hàm nhị phân (sẽ là trường hợp ở đây) trong trường hợp chúng chỉ đơn giản áp dụng hàm bên trong cho một đối số. Vì vậy, thực sự )
trên một chức năng duy nhất g
cho một chức năng tính toán [x, g(y)]
. Sử dụng cái này trong một cái móc trái, cùng với f
, chúng ta có được [f(x), g(y)]
. Điều này, lần lượt được sử dụng trong một bối cảnh đơn nhất, có nghĩa là nó thực sự được gọi với x == y
và vì vậy chúng tôi kết thúc với[f(x), g(x)]
yêu cầu. Phù.
Điều đó chỉ để lại một điều, đó là chức năng kiểm tra trước đó của chúng tôi g
. Hãy nhớ rằng nó sẽ được gọi là g([p, s], x)
nơi x
vẫn là giá trị đầu vào hiện tại, p
là sản phẩm chữ số của nó và s
là tổng số của nó. Đây là g
:
&]&|
<*&d
N
Để kiểm tra tính phân chia, rõ ràng chúng ta sẽ sử dụng modulo, có |
trong Sứa. Đôi khi, một cách bất thường, nó lấy toán hạng bên phải của nó, toán hạng tay trái của nó, có nghĩa là các đối số g
đã được sắp xếp theo thứ tự đúng (các hàm số học như thế này tự động xâu chuỗi các danh sách, vì vậy điều này sẽ tính toán hai mô đun riêng biệt miễn phí) . Số của chúng tôi chia hết cho cả sản phẩm và tổng, nếu kết quả là một cặp số không. Để kiểm tra xem đó có phải là trường hợp không, chúng tôi coi cặp này là một danh sách gồm 2 chữ số cơ bản ( d
). Kết quả của giá trị này là 0, chỉ khi cả hai phần tử của cặp bằng 0, vì vậy chúng ta có thể phủ định kết quả của N
giá trị này ( ) để có được giá trị trung thực cho cả hai giá trị có phân chia đầu vào hay không. Lưu ý rằng |
, d
vàN
chỉ đơn giản là tất cả được sáng tác cùng với một cặp &
s.
Thật không may, đó không phải là câu chuyện đầy đủ. Nếu sản phẩm chữ số bằng 0 thì sao? Chia và modulo bằng 0 đều trả về 0 trong Sứa. Mặc dù điều này có vẻ như là một quy ước hơi kỳ quặc, nhưng nó thực sự có vẻ hữu ích (vì chúng ta không cần kiểm tra số 0 trước khi thực hiện modulo). Tuy nhiên, điều đó cũng có nghĩa là chúng ta có thể nhận được dương tính giả, nếu tổng chữ số không chia đầu vào, nhưng sản phẩm chữ số bằng 0 (ví dụ: đầu vào 10
).
Chúng tôi có thể khắc phục điều này bằng cách nhân kết quả chia hết của chúng tôi với sản phẩm chữ số (vì vậy nếu sản phẩm chữ số bằng 0, nó cũng sẽ biến giá trị trung thực của chúng tôi thành số 0). Hóa ra đơn giản hơn để nhân kết quả chia hết với cặp sản phẩm và tổng, và trích xuất kết quả từ sản phẩm sau đó.
Để nhân kết quả với cặp, chúng ta cần phải lấy lại ở giá trị trước đó (cặp). Điều này được thực hiện với một ngã ba ( ]
). Dĩa giống như móc trên steroid. Nếu bạn cung cấp cho họ hai hàm f
và g
, chúng đại diện cho hàm nhị phân tính toán f(a, g(a, b))
. Trong trường hợp của chúng tôi, a
là cặp sản phẩm / tổng, b
là giá trị đầu vào hiện tại, g
là phép thử chia hết của chúng tôi và f
là phép nhân. Vì vậy, tất cả các tính toán này [p, s] * ([p, s] % x == [0, 0])
.
Tất cả những gì còn lại bây giờ là trích xuất giá trị đầu tiên của giá trị này, đây là giá trị cuối cùng của hàm kiểm tra được sử dụng trong trình vòng lặp. Điều này đơn giản như việc soạn thảo ( &
) ngã ba với hàm head<
, trả về giá trị đầu tiên của danh sách.