Làm cho tôi một ít cà ri


20

Có hàm f lấy các đối số x 1 , x 2 , Hoài, x n

                                               - I E.  f: X 1 × X 2 × ngày × X n → Y

- tách lạng bộ định nghĩa lại f là một hàm tham gia một đối số duy nhất một 1 mà bản đồ để với chức năng khác. Kỹ thuật này hữu ích cho ứng dụng một phần, ví dụ với powchức năng được xử lý, chúng ta có thể viếtexp = pow(e) .

Thí dụ

Giả sử chúng ta có hàm sau f lấy ba đối số ( f: X 1 × X 2 × X 3 → Y ):

def f(a,b,c):
  return a + b * c

Currying hàm này để lại cho chúng ta f_curry: X 1 → (X 2 → (X 3 → Y)) , nếu bây giờ chúng ta gọi hàm đó hai lần với f_curry(1)(2)chúng ta sẽ nhận được một hàm ( h) tương đương với hàm được trả về sau:

def h(c):
   return 1 + 2 * c

Hàm curried fcó thể được viết như thế này (Python 3):

def f_curry(a):
  def g_curry(b):
    def h(c):
      return a + b * c
    return h
  return g_curry

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

Thử thách

Thử thách của bạn sẽ là cà ri một chức năng như được mô tả ở trên, đây là các quy tắc:

  • Đầu vào sẽ là một hàm hộp đen có ít nhất 2 đối số
  • Hàm đầu vào sẽ luôn có một số lượng đối số cố định (không giống printfhoặc tương tự, lưu ý: bạn cần hỗ trợ các hàm với bất kỳ số lượng đối số 2)
  • Nếu ngôn ngữ của bạn sử dụng các hàm được xử lý theo mặc định (ví dụ: Haskell), bạn có thể mong đợi chức năng nhập được xác định qua N -tuples, thay vì "hàm bậc cao hơn"
  • Bạn có thể lấy số lượng đối số làm đầu vào
  • Đầu ra sẽ tương đương với đầu vào *
  • Bạn có thể cho rằng hàm đầu ra sẽ chỉ là:
    • được gọi với ít hơn hoặc bằng số lượng đối số mà hàm đầu vào
    • được gọi với các đối số đúng loại

* Điều này có nghĩa là cho một đầu vào fNđối số và đầu ra hcho tất cả các đối số hợp lệ, a1,…,aNnó giữ điều đó f(a1,a2,…,aN) == h(a1)(a2)…(aN).



vậy đầu vào là def f(a,b,c): return a + b * cvà đầu ra là def f_curry(a): def g_curry(b): def h(c): return a + b * c return h return g_curry?
DanielIndie

@DanielIndie: Nếu bạn lấy ví dụ đó, đầu vào sẽ là f(được xác định ở đâu đó) và đầu ra phải tương đương với f_curry. Hoặc đầu vào sẽ là lambda a,b,c: a+b*cvà đầu ra một chức năng tương đương f_curry.
ბიმო

Điều này khó thực hiện trong hầu hết các ngôn ngữ được nhập tĩnh ... Tôi đoán bạn cần các hàm loại cho việc này.
Paŭlo Ebermann

@ PaŭloEbermann: Đúng, một số ngôn ngữ sẽ không thể giải quyết được nhiệm vụ này (lưu ý lập trình chức năng thẻ ). Tuy nhiên, một số ngôn ngữ được nhập tĩnh có thể có thể sử dụng các con trỏ hàm có thể là I / O hợp lệ, đó chủ yếu là lý do tôi cho phép lấy số lượng đối số làm đầu vào bổ sung.
ბიმო

Câu trả lời:


11

JavaScript (ES6), 35 byte

f=g=>g.length<2?g:a=>f(g.bind(f,a))

9

Idris , 204 byte

import Data.HVect
C:(a:Vect n Type)->(HVect a->Type)->Type
C[]T=T[]
C(h::t)T=(x:h)->C t(T .(x::))
c:{a:Vect n Type}->{T:HVect a->Type}->((b:HVect a)->T b)->C a T
c{a=[]}f=f[]
c{a=h::t}f=\v=>c(\u=>f(v::u))

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

Âm thanh như một công việc cho các loại phụ thuộc! Vâng, có lẽ.


C là hàm loại currying. Cho một vectơ các loại a = [t 1 , t 2 , Giảm t n ] và một hàm loại T: HVect a → Loại , nó trả về một loại mới:

           (x 1  : t 1 ) → (x 2  : t 2 ) → Câu → (T [x 1 , x 2 , cách x n ])

Ở đây, HVectloại vectơ không đồng nhất từ Idris Prelude - loại n -tuples có các phần tử thuộc n loại khác nhau.

c là một chức năng mà phải mất mộtT như các đối số tiềm ẩn, và sau đó chuyển đổi một uncurried chức năng fcủa các loại ((b: HVect a) → T b) thành một cà ri một trong những loại C T .

( C chỉ đơn giản là mô tả những gì chúng ta muốn làm; c thực sự làm điều đó. Nhưng chúng ta không thể tránh khỏi việc không xác định C , vì Idris yêu cầu mọi định nghĩa cấp cao nhất đều có chữ ký loại.)


Liên kết TIO đưa ra một ví dụ sử dụng. Nếu chúng ta định nghĩa một hàm trên 3-tuples (Nat, Nat, String) như sau:

uncurried : HVect [Nat, Nat, String] -> String
uncurried [a, b, c] = show (a*a + b*b) ++ c

sau đó uncurried [3, 4, "th"]mang lại kết quả tương tự như c uncurried 3 4 "th". Idris đưa ra các lập luận a=[Nat, Nat, String]T=const String đối với chúng tôi, tôi tin.

Tôi dựa trên mã này theo ý chính của timjb.


Theo tôi, các bộ dữ liệu trong Haskell và Idris thực sự nên được HVectmặc định HVectlà một bộ dữ liệu mà bạn có thể không biết.
Trái cây Esolanging


5

R , 96 byte

y=function(f,n=length(formals(f)),p=list())function(x,u=c(p,x))`if`(n<2,do.call(f,u),y(f,n-1,u))

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


Phiên bản trước (97 byte)

-1 byte nhờ @JayCE


Tôi không thấy làm thế nào để rút ngắn về cơ bản . Bạn có thể chơi golf ba byte bằng cách loại bỏ các dấu ngoặc nhọn và khoảng trắng ở cuối dòng đầu tiên. Và hai điều nữa do quy ước ở đây không bao gồm tên của hàm trong số byte. TIO
ngm

@ngm Tên hàm phải được đưa vào khi đệ quy.
Ørjan Johansen

@ngm: Tôi đặt câu lệnh if bên trong hàm phụ lưu một phần mười byte :)
digEmAll


3

Python 2 , 60 byte

c=lambda f,n,l=[]:lambda a:n-1and c(f,n-1,l+[a])or f(*l+[a])

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

Chân trang là một trình kiểm tra sử dụng STDIN theo cách sau trên mỗi dòng:

  1. Các chức năng chính nó
  2. Số lượng đối số của hàm, ≥2
  3. Một danh sách các đối số ( [a,b,...])

Lưu ý rằng, trong khi một danh sách các đối số được đưa ra làm đầu vào trong trình kiểm tra, trong thực tế, tương đương bị cong được đưa vào danh sách và danh sách bị giảm theo lệnh gọi hàm.

Một phiên bản 55 byte tương tự đã được cung cấp bởi ovs :

c=lambda f,n,*l:lambda a:n-1and c(f,n-1,*l,a)or f(*l,a)

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


2

Súp lơ , 84 byte

(:= c(\($f$n(@a))(if$n(\($a)(call c(cat(list$f(-$n 1))@a(list$a))))else(call$f@a))))

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


1
Mmm, cà ri súp lơ. Thơm ngon. ^ _ ^
DLosc

@DLosc không có đủ câu trả lời cho thử thách này trong langauges với các tên liên quan đến thực phẩm: P (mặc dù tôi đoán hầu hết trong số họ không thực sự có chức năng)
ASCII - chỉ



1

Tùy viên , 5 byte

Curry

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

Xây dựng đơn giản, phần lớn không thú vị. Nhưng, đây là một phiên bản từ đầu:

Tùy viên, 35 byte

{If[#__2<#_,Fold[`&:,$'__],_@@__2]}

Giải trình:

{If[#__2<#_,Fold[`&:,$'__],_@@__2]}
{                                 }    lambda
 If[       ,              ,      ]     if
    #__2                                 the number of parameters after the first
        <#_                              is less than the arity of the first
            Fold[   ,    ]             then fold:
                 `&:                     right-function bonding
                     $                     over this function
                      '__                  paired with the rest of the parameters
                          ,            otherwise:
                           _@@           call the first parameter
                              __2        with the rest of them

1

Java 8, 46 + 318 = 364 byte

Đây là một lambda được uốn cong (hah) lấy một hàm và đếm đối số và trả về hàm bị cong.

import java.lang.reflect.*;import java.util.*;

f->p->new java.util.function.Function(){Object F=f;Method m=F.getClass().getDeclaredMethods()[0];int P=p;List a=new Stack();public Object apply(Object r){a.add(r);try{return a.size()<P?this:m.invoke(F,a.toArray());}catch(Throwable t){t=t.getCause();if(t instanceof Error)throw(Error)t;else throw(RuntimeException)t;}}}

Dùng thử trực tuyến

Loại đệ trình

Chức năng nhập liệu

Đầu vào hàm là một đối tượng với một phương thức duy nhất (không bao gồm các phương thức được kế thừa) đại diện cho hàm. Lưu ý rằng giao diện chức năng tiêu chuẩn không thể được sử dụng làm loại đầu vào vì các chức năng của (ví dụ) 3 tham số phải được hỗ trợ. Cũng lưu ý rằng một biểu thức lambda chuyển sang java.util.function.Functionloại giống như tiêu chuẩn có thể được thông qua (phương thức duy nhất là apply).

Các ngoại lệ được kiểm tra có thể được khai báo trên hàm đầu vào, nhưng chúng có thể không bị ném (tức là chúng sẽ không được truyền tới người gọi của hàm đầu ra). Điều này được coi là có thể chấp nhận được vì các giao diện chức năng của Java không cho phép các ngoại lệ được kiểm tra (và việc truyền bá chúng sẽ ngăn việc gửi trả lại a Function). Ngoại lệ thời gian chạy (có thể gán cho RuntimeExceptionhoặcError ) được truyền bá.

Chức năng đầu ra

Đầu ra của bài nộp là a java.util.function.Function<Object, Object>. Tôi đã cân nhắc trả lại một đơn vị bằng Objectmột applyphương thức (như trong đầu vào), nhưng sau đó phản xạ sẽ được yêu cầu để gọi kết quả, điều này có vẻ bất tiện đến mức không thể chấp nhận được, đặc biệt, việc gọi tất cả các cách sẽ không còn có thể trong một lần duy nhất biểu hiện.

Sử dụng

Bởi vì đệ trình trả về một hàm từ Objectđến Object, đầu ra có thể được gọi trực tiếp (với apply), nhưng các giá trị trả về trung gian tiếp theo phải được chuyển sang một loại thích hợp (ví dụ java.util.function.Function<Object, Object>) trước khi được gọi. Tham khảo TIO cho một số ví dụ sử dụng.

Lưu ý rằng trong các hàm Java (tức là các phương thức) không phải là các đối tượng hạng nhất. Do đó, cú pháp được sử dụng trong dấu đầu ra của mô tả thách thức là vô nghĩa trong Java. Thay vì f(a1, a2, a3)chúng ta có f.apply(a1, a2, a3), và hơn là f(a1)(a2)(a3)chúng ta có f.apply(a1).apply(a2).apply(a3).

Hạn chế

Khi một kết quả trung gian được áp dụng (một đối số được thêm vào), kết quả thực sự là một bản sao đột biến của kết quả ban đầu. Ví dụ: trong đoạn trích này:

Function<Object, Function<Integer, Function>> submission = ...;
Function c = submission.apply((IntBinaryOperator) (a, b) -> a + b).apply(2);
Function c2 = (Function) c.apply(2);
System.out.println(c2.apply(2));
System.out.println(c2.apply(3));

dòng 4 sẽ in 4, nhưng dòng 5 sẽ thất bại, bởi vì tại thời điểm đó c2đã có các đối số 22(lưu ý rằngc2 == c ). Điều này vi phạm tinh thần cà ri, nhưng đáp ứng yêu cầu cụ thể được nêu trong thử thách.

Ung dung

Xem TIO cho một bản sao vô căn cứ.



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.