คำถามเป็นสองส่วน ประการแรกคือแนวความคิด ต่อไปจะดูคำถามเดียวกันอย่างเป็นรูปธรรมมากขึ้นใน Scala
- การใช้เฉพาะโครงสร้างข้อมูลที่ไม่เปลี่ยนรูปในภาษาโปรแกรมทำให้การใช้อัลกอริทึม / ลอจิกบางอย่างมีราคาแพงกว่าในทางปฏิบัติหรือไม่? สิ่งนี้นำมาสู่ความจริงที่ว่าการไม่เปลี่ยนรูปเป็นหลักการสำคัญของภาษาที่ใช้งานได้อย่างหมดจด มีปัจจัยอื่น ๆ ที่ส่งผลกระทบนี้หรือไม่?
- ลองมาเป็นตัวอย่างที่เป็นรูปธรรมมากขึ้น โดยทั่วไปแล้วQuicksortได้รับการสอนและนำไปใช้โดยใช้การดำเนินการที่ไม่แน่นอนบนโครงสร้างข้อมูลในหน่วยความจำ เราจะนำสิ่งดังกล่าวไปใช้อย่างไรในรูปแบบการทำงานที่บริสุทธิ์โดยมีค่าใช้จ่ายในการคำนวณและพื้นที่จัดเก็บที่เทียบเคียงกับเวอร์ชันที่เปลี่ยนแปลง โดยเฉพาะใน Scala ฉันได้รวมเกณฑ์มาตรฐานคร่าวๆไว้ด้านล่าง
รายละเอียดเพิ่มเติม:
ฉันมาจากพื้นฐานการเขียนโปรแกรมที่จำเป็น (C ++, Java) ฉันได้สำรวจการเขียนโปรแกรมเชิงฟังก์ชันโดยเฉพาะ Scala
หลักการหลักบางประการของการเขียนโปรแกรมเชิงฟังก์ชัน:
- หน้าที่เป็นพลเมืองชั้นหนึ่ง
- ฟังก์ชั่นไม่ได้มีผลข้างเคียงและด้วยเหตุนี้วัตถุโครงสร้างข้อมูล / มีไม่เปลี่ยนรูป
แม้ว่าJVMสมัยใหม่จะมีประสิทธิภาพอย่างมากในการสร้างวัตถุและการรวบรวมขยะมีราคาไม่แพงมากสำหรับวัตถุที่มีอายุสั้น แต่ก็ยังดีกว่าที่จะลดการสร้างวัตถุใช่ไหม อย่างน้อยที่สุดในแอปพลิเคชันเธรดเดียวที่การทำงานพร้อมกันและการล็อกไม่ใช่ปัญหา เนื่องจาก Scala เป็นกระบวนทัศน์แบบไฮบริดเราจึงสามารถเลือกที่จะเขียนโค้ดที่จำเป็นกับวัตถุที่เปลี่ยนแปลงได้หากจำเป็น แต่ในฐานะคนที่ใช้เวลาหลายปีในการพยายามใช้วัตถุซ้ำและลดการจัดสรรให้น้อยที่สุด ฉันต้องการความเข้าใจที่ดีเกี่ยวกับโรงเรียนแห่งความคิดที่ไม่ยอมให้เป็นเช่นนั้น
ในกรณีที่เฉพาะเจาะจงฉันรู้สึกประหลาดใจเล็กน้อยกับข้อมูลโค้ดในบทช่วยสอน 6นี้ มี Quicksort เวอร์ชัน Java ตามด้วยการใช้งาน Scala ที่ดูเรียบร้อยเหมือนกัน
นี่คือความพยายามของฉันในการเปรียบเทียบการใช้งาน ฉันยังไม่ได้ทำโปรไฟล์โดยละเอียด แต่ฉันเดาว่าเวอร์ชัน Scala ช้ากว่าเนื่องจากจำนวนของวัตถุที่จัดสรรเป็นเชิงเส้น (หนึ่งรายการต่อการเรียกซ้ำ) มีโอกาสใดบ้างที่การเพิ่มประสิทธิภาพการโทรหางจะเข้ามามีบทบาท? ถ้าฉันพูดถูก Scala รองรับการเพิ่มประสิทธิภาพการโทรหางสำหรับการโทรซ้ำด้วยตนเอง ดังนั้นควรจะช่วยมันเท่านั้น ฉันใช้ Scala 2.8
เวอร์ชัน Java
public class QuickSortJ {
public static void sort(int[] xs) {
sort(xs, 0, xs.length -1 );
}
static void sort(int[] xs, int l, int r) {
if (r >= l) return;
int pivot = xs[l];
int a = l; int b = r;
while (a <= b){
while (xs[a] <= pivot) a++;
while (xs[b] > pivot) b--;
if (a < b) swap(xs, a, b);
}
sort(xs, l, b);
sort(xs, a, r);
}
static void swap(int[] arr, int i, int j) {
int t = arr[i]; arr[i] = arr[j]; arr[j] = t;
}
}
รุ่น Scala
object QuickSortS {
def sort(xs: Array[Int]): Array[Int] =
if (xs.length <= 1) xs
else {
val pivot = xs(xs.length / 2)
Array.concat(
sort(xs filter (pivot >)),
xs filter (pivot ==),
sort(xs filter (pivot <)))
}
}
Scala Code เพื่อเปรียบเทียบการใช้งาน
import java.util.Date
import scala.testing.Benchmark
class BenchSort(sortfn: (Array[Int]) => Unit, name:String) extends Benchmark {
val ints = new Array[Int](100000);
override def prefix = name
override def setUp = {
val ran = new java.util.Random(5);
for (i <- 0 to ints.length - 1)
ints(i) = ran.nextInt();
}
override def run = sortfn(ints)
}
val benchImmut = new BenchSort( QuickSortS.sort , "Immutable/Functional/Scala" )
val benchMut = new BenchSort( QuickSortJ.sort , "Mutable/Imperative/Java " )
benchImmut.main( Array("5"))
benchMut.main( Array("5"))
ผล
เวลาเป็นมิลลิวินาทีสำหรับการวิ่งติดต่อกันห้าครั้ง
Immutable/Functional/Scala 467 178 184 187 183
Mutable/Imperative/Java 51 14 12 12 12
O(n)
รายการ concat มันสั้นกว่ารุ่น pseudocode;)