param: _ * หมายความว่าอย่างไรใน Scala


87

ยังใหม่สำหรับ Scala (2.9.1) ฉันมีList[Event]และต้องการคัดลอกลงใน a Queue[Event]แต่ไวยากรณ์ต่อไปนี้ให้ผลQueue[List[Event]]แทน:

val eventQueue = Queue(events)

ด้วยเหตุผลบางประการการทำงานต่อไปนี้:

val eventQueue = Queue(events : _*)

แต่ฉันอยากจะเข้าใจว่ามันทำอะไรและทำไมมันถึงได้ผล? ฉันดูลายเซ็นของQueue.applyฟังก์ชันแล้ว:

def apply[A](elems: A*)

และฉันเข้าใจว่าทำไมความพยายามครั้งแรกไม่ได้ผล แต่ความหมายของครั้งที่สองคืออะไร? คืออะไร:และ_*ในกรณีนี้และทำไมapplyฟังก์ชั่นไม่ใช้เพียงแค่Iterable[A]?

คำตอบ:


93

a: Aเป็นคำอธิบายประเภท; ดูจุดประสงค์ของคำอธิบายประเภทใน Scala คืออะไร?

: _* เป็นอินสแตนซ์พิเศษของ ascription ชนิดซึ่งบอกให้คอมไพเลอร์ปฏิบัติต่ออาร์กิวเมนต์เดียวของประเภทลำดับเป็นลำดับอาร์กิวเมนต์ตัวแปรเช่น varargs

มันถูกต้องสมบูรณ์ในการสร้างQueueใช้Queue.applyที่มีองค์ประกอบหนึ่งซึ่งเป็นลำดับหรือ iterable Iterable[A]ดังนั้นนี่คือสิ่งที่เกิดขึ้นเมื่อคุณได้ให้เป็นหนึ่งเดียว


83

นี่เป็นสัญกรณ์พิเศษที่บอกให้คอมไพเลอร์ส่งผ่านแต่ละองค์ประกอบเป็นอาร์กิวเมนต์ของตัวเองแทนที่จะเป็นอาร์กิวเมนต์เดียว ดูที่นี่ .

เป็นคำอธิบายประกอบประเภทที่ระบุอาร์กิวเมนต์ลำดับและถูกกล่าวถึงเป็น "ข้อยกเว้น" ของกฎทั่วไปในส่วน 4.6.2 ของข้อกำหนดภาษา "พารามิเตอร์ที่ซ้ำกัน"

มันจะมีประโยชน์เมื่อมีฟังก์ชั่นใช้เวลาจำนวนตัวแปรของการขัดแย้งเช่นฟังก์ชั่นเช่นdef sum(args: Int*)ซึ่งสามารถเรียกว่าเป็นsum(1), sum(1,2)ฯลฯ หากคุณมีรายการเช่นxs = List(1,2,3)คุณไม่สามารถส่งxsตัวเองเพราะมันเป็นListมากกว่าInt, sum(xs: _*)แต่คุณสามารถส่งผ่านองค์ประกอบของการใช้


def sum(xs: _*)พ่น 'error: unbound wildcard type'
7kemZmani

คำตอบของคุณชัดเจน แต่นี่เป็นการสร้างความสับสนให้กับฉันมากขึ้นโดยปกติในสกาลาxs: intหมายถึงประเภทของ xs คือ int ซึ่งเป็นไวยากรณ์ข้างต้นในสกาลาซึ่งxs: _*หมายความว่า xs ถูกส่งไปยังสมาชิกแต่ละคน
Rpant

ตามลิงค์ด้านบนและดูเหมือนว่ามันคืออะไร type ascription เป็นคำศัพท์สกาลาสำหรับการหล่อประเภท java กรุณาแก้ไขฉันถ้าผิด
Rpant

2
@ 7kemZmani: คุณต้องกำหนดฟังก์ชั่นที่มีความเฉพาะเจาะจงประเภท var-args: def sum(args: Int*)และคุณเรียกมันว่ามีประเภทสัญลักษณ์แทน "ทั่วไป" var-args val a = sum(xs: _*)นี้: ลองนึกดูว่า_*"ฉันกำลังส่ง Int * หรือ String * หรืออะไรก็ได้ * ที่กำหนดไว้ในลายเซ็นของวิธีการ"
Alfonso Nishikawa

10

สำหรับชาว Python:

Scala ของ_*ผู้ประกอบการจะมากหรือน้อยเทียบเท่า ธ-operator *


ตัวอย่าง

การแปลงตัวอย่าง scala จากลิงค์ที่จัดทำโดยLuigi Plinge :

def echo(args: String*) = 
    for (arg <- args) println(arg)

val arr = Array("What's", "up", "doc?")
echo(arr: _*)

ถึง Python จะมีลักษณะดังนี้:

def echo(*args):
    for arg in args:
        print "%s" % arg

arr = ["What's", "up", "doc?"]
echo(*arr)

และทั้งสองให้ผลลัพธ์ต่อไปนี้:

อะไร
ขึ้น
doc?


ความแตกต่าง: การคลายพารามิเตอร์ตำแหน่ง

ในขณะที่*-operator ของ Python สามารถจัดการกับการคลายพารามิเตอร์ตำแหน่ง / พารามิเตอร์สำหรับฟังก์ชัน fixed-arity:

def multiply (x, y):
    return x * y

operands = (2, 4)
multiply(*operands)

8

ทำเช่นเดียวกันกับ Scala:

def multiply(x:Int, y:Int) = {
    x * y;
}

val operands = (2, 4)
multiply (operands : _*)

จะล้มเหลว:

อาร์กิวเมนต์ไม่เพียงพอสำหรับวิธีการคูณ: (x: Int, y: Int) Int
พารามิเตอร์ค่าที่ไม่ระบุ y

แต่เป็นไปได้ที่จะบรรลุสิ่งเดียวกันกับสกาล่า:

def multiply(x:Int, y:Int) = {
    x*y;
}

val operands = (2, 4)
multiply _ tupled operands

ตามLorrin Nelsonนี่คือวิธีการทำงาน:

ส่วนแรก f _ คือไวยากรณ์สำหรับฟังก์ชันที่นำไปใช้บางส่วนซึ่งไม่มีการระบุอาร์กิวเมนต์ใด ๆ สิ่งนี้ทำงานเป็นกลไกในการยึดวัตถุฟังก์ชัน tupled ส่งคืนฟังก์ชันใหม่ซึ่งของ arity-1 ที่ใช้ arity-n tuple ตัวเดียว

การอ่านต่อไป:

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