2010-11-10 47 views
33

Mã-dưới-dữ liệu là gì? Tôi đã nghe nó vượt trội hơn "code-as-ascii-characters" nhưng tại sao? Cá nhân tôi tìm thấy triết lý mã-như-dữ liệu một chút khó hiểu thực sự.Tại sao mã hóa dưới dạng dữ liệu?

Tôi đã xem xét trong Đề án, nhưng tôi chưa bao giờ thực sự nhận được toàn bộ mã dữ liệu và tự hỏi chính xác ý nghĩa của nó là gì?

+1

từ ngữ của bạn là "chủ quan và tranh luận", nhưng đây vẫn là một câu hỏi thú vị. Bạn có thể làm mềm ngôn ngữ của bạn không? –

+0

Vâng, xin lỗi nếu tôi hơi cùn. Sẽ thay đổi. –

Trả lời

51

Điều đó có nghĩa là mã chương trình bạn viết cũng là dữ liệu có thể được điều khiển bởi một chương trình. Thực hiện một biểu thức Đề án đơn giản như

(+ 3 (* 6 7)) 

Bạn có thể coi nó như là một biểu thức toán học khi đánh giá mang lại giá trị. Nhưng đây cũng là danh sách chứa ba phần tử, cụ thể là +, 3(* 6 7). Bởi trích dẫn danh sách,

'(+ 3 (* 6 7)) 

Bạn nói với chương trình để coi đó là sau này, cụ thể là chỉ là một danh sách có chứa ba yếu tố. Do đó, bạn có thể điều khiển danh sách này bằng một chương trình và rồi đánh giá danh sách đó. Sức mạnh nó mang lại cho bạn là rất lớn, và khi bạn "có được" ý tưởng, có một số thủ thuật rất thú vị để được chơi.

+0

Tiện lợi. Tôi sẽ phải nhìn vào đó. –

+18

nhận xét hay nhưng tôi chỉ cần nói: BEST NAME EVER! :) – Quibblesome

+1

cảm ơn câu trả lời này chỉ chụp một cái gì đó trong tâm trí của tôi và bây giờ tôi có ý nghĩa của nhiều điều tôi đọc về Lisp – Uberto

11

Mã dưới dạng dữ liệu đề cập đến thực tế là mã của bạn được thể hiện dưới dạng cấu trúc dữ liệu của ngôn ngữ. Tôi sẽ không cố gắng tranh luận ở đây rằng đó là cách tốt nhất để lập trình, nhưng tôi thấy đó là một cách hay để thể hiện ý tưởng trong đoạn mã.

Một trong những lợi ích là lập trình meta là rất gần giống với lập trình thông thường. Với các ký tự mã as-ascii, bạn thường phải thực hiện một số phân tích cú pháp nghiêm trọng để làm bất cứ điều gì meta, và bạn bỏ qua các bit khó chịu đó với Lisp.

17

Là lập trình viên Lisp, bạn học cách nghĩ về nguồn chương trình dưới dạng dữ liệu. Nó không còn là văn bản tĩnh mà là dữ liệu. Trong một số hình thức của Lisp chương trình chính nó là cấu trúc dữ liệu, được thực hiện.

Sau đó, tất cả các công cụ đều được định hướng theo cách đó. Thay vì một bộ xử lý macro văn bản, Lisp có một hệ thống macro hoạt động trên các chương trình dưới dạng dữ liệu. Việc chuyển đổi các chương trình sang và từ văn bản cũng có các công cụ của nó.

Hãy nghĩ về việc thêm hai yếu tố của một vector:

(let ((v (vector 1 2 3))) 
    (+ (aref v 0) 
     (aref v 1))) 

Không có gì bất thường về nó được. Bạn có thể biên dịch và chạy nó.

Nhưng bạn cũng có thể làm điều này:

(let ((v (vector 1 2 3))) 
    (list '+ 
     (list 'aref v 0) 
     (list 'aref v 1))) 

Đó trả về một danh sách với một biểu tượng dấu cộng và hai danh sách con. Các danh sách con này có ký hiệu làf, sau đó là giá trị mảng của v và giá trị chỉ mục.

Điều đó có nghĩa là chương trình được xây dựng chứa các ký hiệu thực sự, nhưng cũng có dữ liệu. Mảng này thực sự là một phần của danh sách con. Vì vậy, bạn có thể xây dựng chương trình và các chương trình này là dữ liệu và có thể chứa dữ liệu tùy ý.

EVAL sau đó đánh giá chương trình dưới dạng dữ liệu.

CL-USER 17 > (setf *print-circle* t) 
=> T 

Ở trên cho chúng tôi biết máy in nên in cấu trúc dữ liệu tròn sao cho danh tính được giữ nguyên khi đọc lại.

CL-USER 18 > (let ((v (vector 1 2 3))) 
       (list '+ 
        (list 'aref v 0) 
        (list 'aref v 1))) 
=> (+ (AREF #1=#(1 2 3) 0) (AREF #1# 1)) 

Bây giờ chúng ta hãy eval dữ liệu như một chương trình Lisp:

CL-USER 19 > (EVAL (let ((v (vector 1 2 3))) 
        (list '+ 
          (list 'aref v 0) 
          (list 'aref v 1)))) 

=> 3 

Nếu trình biên dịch của bạn hy vọng văn bản như là nguồn người ta có thể xây dựng các văn bản, nhưng họ không bao giờ có thể tham khảo dữ liệu trực tiếp. Đối với việc xây dựng nguồn dựa trên văn bản này, nhiều công cụ đã được phát triển, nhưng nhiều công cụ này có xu hướng hoạt động theo các giai đoạn. Trong Lisp, chức năng thao tác dữ liệu có thể được áp dụng trực tiếp để thao tác các chương trình và chức năng này được tích hợp trực tiếp và một phần của quá trình đánh giá.

Vì vậy, Lisp cung cấp cho bạn thêm mức độ tự do và cách suy nghĩ mới.

+3

Tôi chỉ cần nói điều này: oh đầu của tôi. 'đã ký, lập trình thủ tục' – jcolebrand

+1

@drachenstern: Và thực tế bạn chưa thấy gì cả. Sức mạnh thực sự của mã như dữ liệu đi kèm với các macro. –

+0

@JS Tôi hoàn toàn chắc chắn điều đó. Nhưng ow. Điều đó thổi tâm trí của tôi như nó đã được. – jcolebrand

8

Trong Scheme (hoặc bất kỳ Lisp), bạn có thể khai báo literals danh sách như thế này:

> '(1 2 3) 
=> (1 2 3) 

này cũng tương tự như nhiều ngôn ngữ cấp cao khác, ngoại trừ sự khác biệt nhỏ trong ký hiệu. Ví dụ: đây là cách một số ngôn ngữ khác thể hiện các danh sách văn bản:

[1, 2, 3] # Python 
#(1 2 3) "Smalltalk. This is in fact an array in Smalltalk. Let us ignore that for now." 

Danh sách có thể chứa bất kỳ loại giá trị nào. Vì các hàm là các đối tượng hạng nhất, nên một danh sách cũng có thể chứa các hàm. Hãy để chúng tôi thay thế phần tử đầu tiên trong danh sách ở trên bằng hàm:

> '(+ 2 3) 
=> (+ 2 3) 

Dấu nháy đơn (') xác định danh sách theo nghĩa đen. (Giống như # trong Smalltalk). Điều gì sẽ xảy ra nếu chúng tôi xóa báo giá? Sau đó, trình thông dịch Scheme sẽ xử lý danh sách đặc biệt. Nó sẽ xem xét phần tử đầu tiên như một hàm (hoặc thủ tục) và phần còn lại của các phần tử làm đối số cho hàm đó. Hàm được thực hiện (hoặc được đánh giá):

> (+ 2 3) 
=> 5 

Khả năng biểu diễn mã thực thi bằng cấu trúc dữ liệu trong ngôn ngữ sẽ mở ra một khả năng mới - chúng ta có thể viết chương trình viết chương trình. Điều đó có nghĩa, các phần mở rộng yêu cầu thay đổi đối với trình biên dịch và hệ thống thời gian chạy bằng các ngôn ngữ khác có thể được triển khai trong Lisp, như một vài dòng của Lisp. Hãy tưởng tượng bạn cần một cấu trúc điều khiển mới trong ngôn ngữ của bạn được gọi là when. Nó tương tự như if nhưng làm cho đọc mã một chút tự nhiên hơn trong một số trường hợp:

(when this-is-true do-this) 

Bạn có thể mở rộng hệ thống Lisp của bạn để hỗ trợ when bằng cách viết một macro ngắn:

(defmacro when (condition &rest body) 
    `(if ,condition (progn ,@body))) 

Vĩ mô là gì nhưng một danh sách, được mở rộng vào thời gian biên dịch. Các cấu trúc ngôn ngữ phức tạp hơn hoặc thậm chí toàn bộ mô hình có thể được thêm vào ngôn ngữ chính sử dụng các danh sách đó. Ví dụ: CLOS, Hệ thống đối tượng chung của Lisp về cơ bản là tập hợp các macro được viết bằng Common Lisp.

36

Mã dưới dạng dữ liệu thực sự chỉ là một mặt của đồng xu. Khác là dữ liệu dưới dạng mã.

Khả năng nhúng dữ liệu tùy ý vào mã Lisp tải và tải lại khi đang di chuyển khiến dữ liệu trở nên rất thuận tiện để xử lý vì nó có thể loại bỏ bất kỳ trở kháng không phù hợp nào giữa cách dữ liệu được biểu diễn và cách mã hoạt động.

Hãy để tôi cung cấp cho bạn một ví dụ.

Giả sử bạn muốn viết một số loại trò chơi máy tính với các lớp quái vật khác nhau. Về cơ bản bạn có hai lựa chọn: mô hình hóa các lớp con quái vật trong ngôn ngữ lập trình của bạn hoặc sử dụng một cách tiếp cận theo hướng dữ liệu trong đó các mô tả lớp được đọc từ một tệp XML.

Làm mô hình hóa trong ngôn ngữ lập trình có những lợi ích dễ sử dụng và đơn giản (luôn là điều tốt). Nó cũng dễ dàng để xác định hành vi tùy chỉnh tùy thuộc vào lớp quái vật khi cần thiết. Cuối cùng, việc triển khai có lẽ là khá tối ưu.

Mặt khác, tải mọi thứ từ tệp dữ liệu linh hoạt hơn nhiều. Bạn có thể làm nhiều thừa kế nơi ngôn ngữ không hỗ trợ nó; bạn có thể gõ động; bạn có thể tải và tải lại mọi thứ vào thời gian chạy; bạn có thể sử dụng cú pháp đơn giản, to-the-point, tên miền cụ thể và hơn thế nữa. Nhưng bây giờ bạn cần phải viết một số loại môi trường thời gian chạy cho toàn bộ điều, và xác định hành vi có nghĩa là tách dữ liệu giữa các tệp dữ liệu và mã trò chơi hoặc nhúng ngôn ngữ kịch bản, một lớp phức tạp khác.

Hoặc bạn có thể thực hiện theo cách của Lisp: chỉ định ngôn ngữ phụ của riêng bạn, dịch mã đó thành mã và thực thi nó. Nếu ngôn ngữ lập trình bạn đang sử dụng đủ linh hoạt và cú pháp linh hoạt, bạn sẽ nhận được tất cả lợi ích từ việc sử dụng phương pháp theo hướng dữ liệu (vì mã là dữ liệu) kết hợp với sự đơn giản của việc giữ mọi thứ trong mã (vì dữ liệu là mã).

Điều này không cụ thể đối với Lisp, nhân tiện. Có nhiều sắc thái khác nhau của mã màu xám dữ liệu tương đương ở giữa Lisp và, nói, C++. Ruby, ví dụ, làm cho việc nhúng dữ liệu trong ứng dụng dễ dàng hơn Python, và Python làm cho nó dễ dàng hơn so với Java. Cả dữ liệu dưới dạng mã và mã-dưới-dữ liệu đều liên tục hơn là một trong hai câu hỏi.

+4

+1 cho mặt khác của đồng xu. –

7

Trừ khi bạn đang sử dụng một cái gì đó giống như một cũ Harvard Mark I, mã của bạn lưu trữ trong cùng một vị trí và cách thức như dữ liệu của bạn - chỉ (như bạn đã nói) có thể dưới dạng các ký tự ASCII, do đó, nó thực sự khó khăn để làm bất cứ điều gì với. Rất có thể, hầu hết các lập trình viên Java không bao giờ tự phân tích cú pháp mã Java.

Nhìn vào bất kỳ chương trình nào - có rất nhiều thông tin khổng lồ (tốt, tùy thuộc vào chương trình!) Được mã hóa trong chính mã nguồn. Đó là lý do của nó cho hiện tại! Bằng cách không sử dụng một ngôn ngữ đồng âm, bạn hoàn toàn nói rằng bạn không thể đọc được từ một chương trình khác mà bạn viết (hoặc OK rằng thật khó mà không ai biết được). Về cơ bản, chương trình duy nhất trên máy tính của bạn có thể đọc nó là trình biên dịch, và điều duy nhất nó có thể làm sau khi đọc là tạo mã đối tượng và thông báo lỗi.

Giả sử bạn phải làm việc với một số nguồn dữ liệu khác mỗi ngày, như tệp XML hoặc RDBMS và cách duy nhất để truy cập dữ liệu đó là chạy nó thông qua "trình biên dịch". đọc. Tôi không nghĩ ai sẽ cho rằng đó là một ý kiến ​​hay.:-)

Tôi thực sự không biết nơi tôi sẽ với điều này, vì vậy tôi sẽ cố gắng tóm tắt lời huyên thuyên trên của tôi:

  • tôi thấy mã như dữ liệu như chỉ là logic tiếp theo bước từ Kiến trúc Harvard để Von Kiến trúc Neumann
  • chúng tôi đã có X-as-dữ liệu cho khá nhiều mỗi khác X, vì vậy nó có vẻ kỳ lạ để loại trừ một loại dữ liệu mà các lập trình viên dành cả ngày thao tác
Các vấn đề liên quan