ทำให้ฉันแกง


20

มีฟังก์ชั่นfที่รับอาร์กิวเมนต์x 1 , x 2 , …, x n

                                               - เช่น  f: X 1 × X 2 ×…× X n → Y

- ความดีความชอบนิยามใหม่ของFเป็นฟังก์ชั่นการใช้อาร์กิวเมนต์เดียว1ซึ่งแมปไปยังฟังก์ชั่นอื่น เทคนิคนี้มีประโยชน์สำหรับการใช้งานบางส่วนเช่นฟังก์ชั่นcurried ที่เราสามารถเขียนได้powexp = pow(e)

ตัวอย่าง

สมมติว่าเรามีฟังก์ชั่นfต่อไปนี้รับสามอาร์กิวเมนต์ ( f: X 1 × X 2 × X 3 → Y ):

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

การปิดฟังก์ชั่นนี้ทำให้เรามีf_curry: X 1 → (X 2 → (X 3 → Y))ถ้าเราจะเรียกฟังก์ชันนั้นสองครั้งf_curry(1)(2)เราจะได้รับฟังก์ชัน ( h) เทียบเท่ากับการคืนค่าต่อไปนี้:

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

ฟังก์ชั่น curried fสามารถเขียนได้เช่นนี้ (Python 3):

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

ลองออนไลน์!

ท้าทาย

ความท้าทายของคุณคือการทำหน้าที่ตามที่อธิบายไว้ข้างต้นนี่คือกฎ:

  • อินพุตจะเป็นฟังก์ชันแบล็กบ็อกซ์ซึ่งมีอาร์กิวเมนต์อย่างน้อย 2 ข้อ
  • ฟังก์ชั่นอินพุตจะมีจำนวนอาร์กิวเมนต์ที่คงที่เสมอ (ไม่เหมือนprintfหรือคล้ายกันหมายเหตุ: คุณต้องรองรับฟังก์ชั่นที่มีอาร์กิวเมนต์จำนวนใด ๆ ≥2)
  • หากภาษาของคุณใช้ฟังก์ชัน curried ตามค่าเริ่มต้น (เช่น Haskell) คุณอาจคาดหวังว่าฟังก์ชันอินพุตจะถูกกำหนดเหนือN -tuples แทนที่จะเป็น "ฟังก์ชันลำดับสูงกว่า"
  • คุณอาจใช้จำนวนอาร์กิวเมนต์เป็นอินพุต
  • เอาต์พุตจะเท่ากับค่าอินพุตที่ curried *
  • คุณอาจสันนิษฐานว่าฟังก์ชั่นการส่งออกจะเป็นเพียง:
    • เรียกว่ามีน้อยหรือเท่ากับจำนวนของอาร์กิวเมนต์ที่ฟังก์ชั่นการป้อนข้อมูลจะใช้เวลา
    • เรียกว่ามีข้อโต้แย้งของประเภทที่เหมาะสม

* นี้จะหมายถึงสำหรับการป้อนข้อมูลfที่มีNข้อโต้แย้งและเอาท์พุทhว่าสำหรับข้อโต้แย้งที่ถูกต้องทั้งหมดก็ถือได้ว่าa1,…,aNf(a1,a2,…,aN) == h(a1)(a2)…(aN)


ที่เกี่ยวข้อง
ბიმო

ดังนั้นอินพุตdef f(a,b,c): return a + b * cและเอาต์พุตคือdef f_curry(a): def g_curry(b): def h(c): return a + b * c return h return g_curryอะไร
DanielIndie

@DanielIndie: ถ้าคุณกำลังการเช่นการป้อนข้อมูลที่จะเป็นf(ซึ่งถูกกำหนดไว้ที่ใดที่หนึ่ง) f_curryและการส่งออกควรเป็นสิ่งที่เทียบเท่ากับ หรือการป้อนข้อมูลที่จะเป็นและเอาท์พุทเทียบเท่าฟังก์ชั่นlambda a,b,c: a+b*c f_curry
ბიმო

นี่เป็นเรื่องยากที่จะทำในภาษาที่พิมพ์แบบคงที่ส่วนใหญ่ ... ฉันคิดว่าคุณต้องการฟังก์ชั่นประเภทสำหรับสิ่งนี้
Paŭlo Ebermann

@ PaŭloEbermann: จริงแล้วบางภาษาไม่สามารถแก้ปัญหานี้ได้ (สังเกตการทำงานของแท็กการเขียนโปรแกรม ) อย่างไรก็ตามบางภาษาที่พิมพ์แบบสแตติกอาจสามารถใช้พอยน์เตอร์ของฟังก์ชั่นซึ่งจะเป็น I / O ที่ถูกต้องซึ่งส่วนใหญ่เป็นเหตุผลที่ฉันอนุญาตให้ใช้จำนวนอาร์กิวเมนต์เป็นอินพุตเพิ่มเติม
ბიმო

คำตอบ:



9

ไอดริส , 204 ไบต์

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))

ลองออนไลน์!

เสียงเหมือนงานประเภทที่ขึ้นอยู่กับ! อาจจะ


Cเป็นฟังก์ชั่นประเภทแกง เมื่อระบุเวกเตอร์ประเภทa = [t 1 , t 2 , … t n ]และฟังก์ชันประเภทT: HVect a → Typeจะส่งคืนชนิดใหม่:

           (x 1  : t 1 ) → (x 2  : t 2 ) →…→ (T [x 1 , x 2 , … x n ])

ที่นี่HVectเป็นชนิดเวกเตอร์ที่ต่างกันจาก Idris Prelude - ชนิดของn -tuples ที่มีองค์ประกอบต่างกันnชนิด

เป็นฟังก์ชั่นที่ใช้และTเป็นข้อโต้แย้งโดยปริยายแล้วแปลงuncurriedฟังก์ชั่นประเภท((ข: HVect ก) → T ข)เป็นแกงกะหรี่หนึ่งในประเภทC เสื้อf

( Cเพียงอธิบายสิ่งที่เราต้องการทำ; cทำได้จริง แต่เราไม่สามารถหลีกเลี่ยงได้โดยไม่กำหนดCเนื่องจาก Idris เรียกร้องให้คำจำกัดความระดับบนสุดทุกคนมีลายเซ็นประเภท)


ลิงก์ TIO แสดงตัวอย่างการใช้งาน หากเรากำหนดฟังก์ชันบน 3-tuples (Nat, Nat, String)ดังต่อไปนี้:

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

แล้วผลตอบแทนถัวเฉลี่ยผลเช่นเดียวกับuncurried [3, 4, "th"] c uncurried 3 4 "th"ไอดริสอ้างถึงข้อโต้แย้งa=[Nat, Nat, String]และT=const Stringสำหรับเราฉันเชื่อว่า

ฉันใช้รหัสนี้ในส่วนสำคัญนี้โดย timjb


ในความคิดของฉัน tuples ใน Haskell และไอดริสจริงควรจะเป็นHVectโดย default- HVectเป็นหลัก tuple ที่คุณสามารถ uncons
แยกผลไม้


5

R , 96 ไบต์

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))

ลองออนไลน์!


รุ่นก่อนหน้า (97 ไบต์)

-1 ไบต์ขอบคุณ @JayCE


ฉันไม่เห็นวิธีการตัดทอนมันขั้นพื้นฐาน คุณสามารถตีสามไบต์ด้วยการกำจัดเครื่องหมายวงเล็บและช่องว่างที่ท้ายบรรทัดแรก และอีกสองเนื่องจากการประชุมที่นี่ไม่รวมชื่อของฟังก์ชั่นในการนับไบต์ TIO
ngm

@ngm ชื่อฟังก์ชั่นจะต้องถูกรวมไว้เมื่อมีการเรียกซ้ำ
Ørjan Johansen

@ngm: ผมใส่คำสั่งถ้าภายในย่อยฟังก์ชั่นการประหยัดในสิบของไบต์ :)
digEmAll


3

Python 2 , 60 ไบต์

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

ลองออนไลน์!

ส่วนท้ายเป็นเครื่องมือทดสอบที่ใช้ STDIN ด้วยวิธีต่อไปนี้ต่อบรรทัด:

  1. ฟังก์ชั่นนั้นเอง
  2. จำนวนอาร์กิวเมนต์ของฟังก์ชัน≥2
  3. รายการข้อโต้แย้ง ( [a,b,...])

โปรดทราบว่าในขณะที่รายการอาร์กิวเมนต์จะได้รับเป็นอินพุตในเครื่องทดสอบ แต่ในความเป็นจริงแล้ว curried ที่เทียบเท่าจะได้รับการต่อเติมในรายการและรายการจะลดลงตามการเรียกใช้ฟังก์ชัน

รุ่น 55 ไบต์ที่คล้ายกันได้รับการจัดเตรียมโดยovs :

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

ลองออนไลน์!


2

กะหล่ำดอก , 84 ไบต์

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

ลองออนไลน์!


1
อืมแกงกะหล่ำดอก อร่อย. ^ _ ^
DLosc

@DLosc มีคำตอบไม่เพียงพอสำหรับความท้าทายนี้ใน langauges ที่มีชื่อที่เกี่ยวข้องกับอาหาร: P (แม้ว่าฉันเดาว่าส่วนใหญ่ของพวกเขาไม่มีฟังก์ชั่นจริง ๆ )
ASCII เท่านั้น

2

Perl 6 , 42 40 ไบต์

my&c={.count>1??{c(.assuming($^a))}!!$_}

ลองออนไลน์!

-2 ไบต์ขอบคุณที่b2gills แบรดกิลเบิร์


คุณไม่จำเป็นต้องใช้ต่อท้ายก็เป็นสิ่งจำเป็นเฉพาะในกรณีที่มีบางสิ่งบางอย่างหลังจากที่มันชอบ* .assuming(*,1)
แบรดกิลเบิร์ต b2gills


1

ทูตขนาด 5 ไบต์

Curry

ลองออนไลน์!

เรียบง่ายในตัวส่วนใหญ่ไม่น่าทึ่ง แต่นี่เป็นรุ่นตั้งแต่ต้น:

ทูต, 35 ไบต์

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

คำอธิบาย:

{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 ไบต์

นี่คือแลมบ์ดา (curated hah) ที่รับฟังก์ชั่นและการนับจำนวนและส่งคืนฟังก์ชั่น curried

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;}}}

ลองใช้ออนไลน์

ประเภทการส่ง

ฟังก์ชั่นอินพุต

ฟังก์ชั่นอินพุตเป็นวัตถุที่มีวิธีการเดียว (ไม่รวมวิธีการสืบทอด) เป็นตัวแทนของฟังก์ชั่น โปรดทราบว่าไม่สามารถใช้ส่วนต่อประสานการทำงานมาตรฐานเป็นประเภทอินพุตได้เนื่องจากฟังก์ชั่นของ (เช่น) พารามิเตอร์ 3 ตัวต้องได้รับการสนับสนุน โปรดทราบด้วยว่าการแสดงออกของแลมบ์ดาที่ส่งไปยังjava.util.function.Functionประเภทที่เหมือนมาตรฐานอาจส่งผ่านได้ (วิธีการเดียวคือapply)

อาจมีการประกาศข้อยกเว้นที่ตรวจสอบบนฟังก์ชันอินพุต แต่อาจไม่ถูกส่งออกไป (เช่นจะไม่ถูกส่งไปยังผู้เรียกของฟังก์ชันเอาต์พุต) สิ่งนี้สันนิษฐานว่าเป็นที่ยอมรับได้เนื่องจากส่วนต่อประสานการทำงานของ Java ไม่อนุญาตให้มีข้อยกเว้นที่ตรวจสอบ (และการเผยแพร่สิ่งเหล่านี้จะป้องกันไม่ให้การส่งคืนFunction) ข้อยกเว้นรันไทม์ (กำหนดให้กับRuntimeExceptionหรือError) มีการเผยแพร่

ฟังก์ชั่นการส่งออก

java.util.function.Function<Object, Object>ผลลัพธ์ของการส่งเป็น ฉันถือว่าการส่งคืนที่ราบObjectด้วยapplyวิธีการ (เช่นในอินพุต) แต่จากนั้นการสะท้อนกลับจะต้องเรียกผลลัพธ์ซึ่งดูเหมือนไม่สะดวกพอที่จะไม่อนุญาตให้ทำได้ การแสดงออก

การใช้

เนื่องจากการส่งคืนฟังก์ชันจากObjectถึงObjectเอาต์พุตอาจถูกเรียกใช้โดยตรง (ด้วยapply) แต่ค่าส่งคืนระดับกลางที่ตามมาจะต้องถูกส่งไปยังประเภทที่เหมาะสม (เช่นjava.util.function.Function<Object, Object>) ก่อนที่จะถูกเรียกใช้ ศึกษา TIO สำหรับตัวอย่างการใช้งาน

โปรดทราบว่าในฟังก์ชั่น Java (เช่นวิธีการ) ไม่ใช่วัตถุชั้นหนึ่ง ดังนั้นไวยากรณ์ที่ใช้ในสัญลักษณ์แสดงผลของคำอธิบายการท้าทายจึงไม่มีความหมายใน Java มากกว่าf(a1, a2, a3)ที่เรามีf.apply(a1, a2, a3)และมากกว่าที่เรามีf(a1)(a2)(a3)f.apply(a1).apply(a2).apply(a3)

ข้อ จำกัด

เมื่อมีการใช้ผลลัพธ์ระดับกลาง (เพิ่มอาร์กิวเมนต์) ผลลัพธ์นั้นเป็นสำเนาที่กลายพันธุ์ของผลลัพธ์ดั้งเดิม ตัวอย่างเช่นในตัวอย่างนี้:

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));

บรรทัดที่ 4 จะพิมพ์4แต่บรรทัดที่ 5 จะล้มเหลวเนื่องจากเวลานั้นc2มีอาร์กิวเมนต์แล้ว2และ2(หมายเหตุด้วยc2 == c) สิ่งนี้ละเมิดวิญญาณของการแกง แต่เป็นไปตามข้อกำหนดเฉพาะที่ระบุไว้ในการท้าทาย

Ungolfed

ดู TIO สำหรับสำเนาที่ไม่ได้รับความนิยม



0

APL (Dyalog Classic) , 58 57 ไบต์

r←(a f g)x
:If a[1]=≢a
rg 1a,x
:Else
r←(a,x)f g
:End

ลองออนไลน์!

การเรียกไวยากรณ์ (พร้อมฟังก์ชั่น curried g, อาร์กิวเมนต์x1ผ่านx3และจำนวนอาร์กิวเมนต์n):

((n x1 f g) x2) x3

ต้องใช้ ⎕IO←1

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.