Apa perbedaan antara `(mcons (mcons '() 25) 16)` dan `(mcons 25 (mcons 16 `()))`

Saya sibuk dengan Struktur dan Interpretasi Latihan Program Komputer 2.18. Di sini kita harus mendefinisikan prosedur kebalikan untuk membalikkan daftar. Ini harus melakukan hal berikut:

(reverse (list 1 4 9 16 25))
;; => (25 16 9 4 1)

Saya mendapatkan definisi berikut:

(define (reverse list)
  (if (null? list) 
      list
      (cons (reverse (cdr list)) (car list))))
;; => (mcons (mcons (mcons (mcons (mcons '() 25) 16) 9) 4) 1).

Kemudian di solusi Di temukan sesuatu yang serupa sebagai berikut:

(define (reverse items) 
  (if (null? (cdr items)) 
      items 
      (append (reverse (cdr items)) 
              (cons (car items) nil))))
;; => (mcons 25 (mcons 16 (mcons 9 (mcons 4 (mcons 1 '()))))).

Ada perbedaan antara append dan cons di sini yang tidak dapat saya jelaskan.

Pertanyaan saya: apa bedanya dan mengapa hasilnya tidak ditampilkan sebagai (25 16 9 4 1)?


person Erwin Rooijakkers    schedule 17.01.2015    source sumber


Jawaban (1)


Jawaban singkatnya: versi pertama reverse salah membuat daftar yang tidak tepat, versi kedua tidak efisien membuat daftar yang tepat. Kita bisa melakukan lebih baik selama kita memahami perbedaan antara append dan cons.

append menggabungkan dua daftar. Jika kita menggunakannya hanya untuk menambahkan elemen di akhir satu daftar, kita akan melakukan lebih banyak pekerjaan daripada yang dibutuhkan: kita harus melintasi seluruh daftar setiap kali hanya untuk meletakkan satu elemen terakhir (lihat: Algoritma Schlemiel the Painter). Oleh karena itu, implementasi reverse yang menggunakan append dapat memiliki kompleksitas yang sama buruknya dengan O(n^2).

Di sisi lain, cons menambahkan satu elemen ke bagian atas daftar, untuk kompleksitas O(n) dalam implementasi reverse. Secara umum, dalam Skema Anda harus mencoba menghindari penggunaan append untuk membuat daftar keluaran baru, selalu pilih cons. Sekarang mari kita lihat apa yang dikembalikan oleh algoritme Anda menggunakan cons:

(reverse '(1 2 3 4 5))
=> '(((((() . 5) . 4) . 3) . 2) . 1)

Mengapa demikian? karena untuk membuat daftar yang tepat dengan cons argumen kedua harus berupa daftar lain yang tepat, tetapi Anda meneruskan satu elemen. Implementasi yang benar memerlukan parameter akumulator - dan kebetulan, ini lebih efisien, karena menggunakan rekursi ekor, sebuah konsep yang seharusnya sudah Anda pahami, seperti yang diperkenalkan di bagian 1.2 buku ini. Coba ini:

(define (reverse lst acc)
  (if (null? lst)
      acc
      (reverse (cdr lst) (cons (car lst) acc))))

(reverse '(1 2 3 4 5) '())
=> '(5 4 3 2 1)

Untuk bagian terakhir dari pertanyaan: daftar ditampilkan dengan mcons (m berarti sel cons dapat diubah) karena bahasa yang Anda gunakan, coba beralih ke #lang racket, yang menggunakan < em>tidak dapat diubah cons secara default, dan akan dicetak seperti yang Anda harapkan.

person Óscar López    schedule 17.01.2015
comment
Terima kasih banyak! Saya menggunakan #lang planet neil/sicp, tetapi akan beralih kembali ke #lang racket. - person Erwin Rooijakkers; 18.01.2015
comment
@ user2609980 namun jangan beralih terlalu cepat, neil/sicp baik-baik saja untuk mengerjakan latihan SICP, sedangkan Racket akan memiliki beberapa keanehan. Akan lebih baik jika Anda membiasakan diri dengan daftar yang dicetak dengan semua mcons ditampilkan. - person Óscar López; 18.01.2015