2009-07-13 26 views
5

Gần đây tôi đã có sự cần thiết phải viết lại một hàm javascript trong javascript, động. Sự dễ dàng mà tôi đã làm nó, và nó thật thú vị, làm tôi kinh ngạc.Trong ngôn ngữ nào bạn có thể tự động viết lại các chức năng khi đang di chuyển?

Trên đây tôi đã có một số HTML:

<div id="excelExport1234" 
    onclick="if(somestuff) location.href='http://server/excelExport.aspx?id=56789&something=else'; else alert('not important');" 
    >Click here to export to excel</div> 

Và tôi không thể thay đổi HTML outputted, nhưng tôi cần phải thêm một tham số phụ để liên kết đó. Tôi bắt đầu suy nghĩ về điều đó và nhận ra rằng tôi chỉ có thể làm điều này:

excelExport = $('excelExport1234'); 
if (needParam) 
     eval('excelExport.onclick = ' + excelExport.onclick.toString().replace("excelReport.aspx?id", "excelReport.aspx?extraParam=true&id") + ';'); 
else 
     eval('excelExport.onclick = ' + excelExport.onclick.toString().replace("extraParam=true&", "") + ';'); 

Và nó hoạt động như một nhà vô địch! excelExport.onclick trả về một đối tượng hàm mà tôi chuyển đổi thành một chuỗi và thực hiện một số thao tác chuỗi. Vì nó bây giờ ở dạng "function() {...}", tôi chỉ quay lại và gán nó cho sự kiện onclick của đối tượng dom. Đó là một chút xấu xí phải sử dụng eval, nhưng AFAIK không có một hàm dựng hàm javascript có thể lấy một chuỗi mã và biến nó thành một đối tượng độc đáo.

Dù sao, quan điểm của tôi không phải là tôi siêu thông minh (tôi không), quan điểm của tôi là điều này thật thú vị. Và tôi biết javascript không phải là ngôn ngữ duy nhất có thể làm được điều này. Tôi đã nghe nói rằng lisp đã có macro trong nhiều năm cho mục đích chính xác này. Ngoại trừ các macro thực sự grok bạn cần phải thực sự grok lisp, và tôi không grok nó, tôi chỉ 'loại nhận được nó'.

Vì vậy, câu hỏi của tôi là: Trong những ngôn ngữ nào bạn có thể (dễ dàng) tự động viết lại các hàm và bạn có thể chỉ cho tôi một ví dụ đơn giản không? Tôi muốn xem nơi nào khác bạn có thể làm điều này và cách thực hiện!

(còn, tôi không có ý tưởng gì để gắn thẻ này như, vì vậy tôi đã dự đoán ngẫu nhiên)

+7

Bằng cách đó là sự điên rồ. – skaffman

+0

Có thể là cộng đồng wiki? – ChristopheD

+0

Tôi chưa bao giờ nói đó là điều thanh lịch nhất để làm, nhưng nó khá tuyệt. Nó giống như hầu hết các hacks twiddling bit - nguy hiểm gây ra họ có thể dựa vào triển khai trình biên dịch và như vậy, nhưng vẫn thông minh và thú vị. –

Trả lời

1

tôi đã sử dụng để làm điều này tất cả các thời gian trong TCL, đó là một việc đơn giản và làm việc tuyệt vời. Tôi có thể điều tra giao diện somethings qua mạng và sau đó tạo ra một giao diện tùy chỉnh được thực hiện trên bay để truy cập và kiểm soát mọi thứ. Ví dụ: bạn có thể tạo giao diện SNMP tùy chỉnh từ thư viện SNMP chung.

Tôi chưa sử dụng, nhưng C# có một số hỗ trợ tích hợp để tạo mã byte riêng của nó, điều này khá ấn tượng.

Tôi đã thực hiện loại điều này trong C là tốt, nhưng có nó không phải là di động và hầu như không bao giờ có giá trị phức tạp. Đó là một kỹ thuật được sử dụng đôi khi cho mã "tự tối ưu hóa" để tạo ra hàm C thích hợp để xử lý tối ưu một tập dữ liệu đã cho.

+1

Trong Tcl, bạn thậm chí có thể xác định lại cấu trúc điều khiển tích hợp cho tính linh hoạt/điên rồ cuối cùng (xóa theo khẩu vị) :-) –

9

LISP là ngôn ngữ cuối cùng tại đây. Các hàm LISP là các danh sách LISP thực tế, có nghĩa là bạn có thể thao tác mã nguồn LISP như thể nó là bất kỳ cấu trúc dữ liệu nào khác.

Dưới đây là một ví dụ rất nhỏ về cách hoạt động:

(define hi 
    (lambda() (display "Hello World\n"))) 
;; Displays Hello World 
(hi) 
(set! hi 
     (lambda() (display "Hola World\n"))) 
;; Displays Hola World 
(hi) 

này, tuy nhiên, có thể trong bất kỳ ngôn ngữ mà chức năng là đối tượng hạng nhất. Một trong những giới thiệu thú vị nhất về sức mạnh của cú pháp này cho LISP là trong hệ thống macro của nó. Tôi thực sự không cảm thấy tôi có thể làm công lý chủ đề, vì vậy đọc những liên kết này nếu bạn quan tâm: đủ

http://en.wikipedia.org/wiki/Macro_(computer_science)#Lisp_macros

http://cl-cookbook.sourceforge.net/macros.html

+1

Các hàm Lisp không phải là danh sách. Các biểu thức xác định hàm là các danh sách, nhưng các hàm là các kiểu đối tượng riêng biệt. –

+5

Gọi Lisp "LISP" là sooooo thế kỷ 20. :-) –

1

dễ dàng trong Perl.

*some_func = sub($) { 
    my $arg = shift; 
    print $arg, "\n"; 
}; 
some_func('foo'); 

yêu cầu Re Sam Saffron của:

*hello_world = sub() { 
    print "oops"; 
}; 
hello_world(); 
*hello_world = sub() { 
    print "hello world"; 
}; 
hello_world(); 
+0

hỗn loạn, bạn có thể viết lại mẫu ruby ​​của tôi trong Perl không? (xem câu trả lời của tôi) –

+0

Uh, cái ngắn tôi hy vọng? – chaos

+0

tôi đã cho bạn một +1 nhưng tôi đã có nghĩa là một trong những dài :) –

2

Self-modifying code cũng được gọi là mã thoái hóa. Điều này thường được coi là một điều xấu, và nó từng là một mục tiêu của ngôn ngữ cấp cao để ngăn không cho nó được viết dễ dàng.

Đây là từ mục wikipedia:

đang tự điều chỉnh được xem bởi một số như một thói quen xấu mà làm cho mã khó hơn để đọc và duy trì. Tuy nhiên, có những cách mà tự sửa đổi được coi là chấp nhận được, chẳng hạn như khi các con trỏ con thường được thay đổi động - ngay cả khi hiệu ứng gần giống với sửa đổi trực tiếp.

+0

Tôi đồng ý rằng bạn chắc chắn không nên làm điều này trong những hoàn cảnh bình thường. Thật là một cơn ác mộng bảo trì. Nhưng như đã đề cập, nó mở một số cánh cửa thực sự gọn gàng - ví dụ, có một số ứng dụng CS hấp dẫn cho mã phát triển để giải quyết vấn đề. –

+2

Câu trả lời này, không trả lời câu hỏi OP. –

3

Tôi đoán nó phụ thuộc vào chính xác những gì bạn xác định là "viết lại động dễ dàng". Ví dụ trong. Net bạn có kiểu Func và lambdas cho phép bạn định nghĩa các hàm như các biến hoặc các hàm ẩn danh tạm thời, ví dụ.

int[] numbers = {1, 2, 3, 4, 5}; 

Func<int[], int> somefunc; 
if (someCondition) 
{ 
    somefunc = (is => is.Sum()); 
} else { 
    somefunc = (is => is.Count()); 
} 

Console.WriteLine(somefunc(numbers).ToString()); 

Trên đây là một ví dụ rất giả tạo của một trong hai cách đếm các mục trong một mảng các số nguyên hoặc tổng hợp sau đó sử dụng chức năng tự động tạo ra phụ thuộc vào một số điều kiện tùy ý.

Lưu ý - Xin đừng chỉ ra rằng những điều này có thể dễ dàng thực hiện mà không cần lambdas (mà họ rõ ràng là có thể) Tôi đã chỉ cần cố gắng để viết một ví dụ rất đơn giản để chứng minh khái niệm trong C#

2

tôi nghĩ rằng đó là trường hợp trong hầu hết các ngôn ngữ động. Dưới đây là một ví dụ bằng Python

 
def f(x): 
    print x 

def new_function(x): print "hello", x 

f("world")  
f = new_function 
f("world") 

Đầu ra là

 
world 
hello world 

Tôi nghĩ rằng kỹ thuật này nên được sử dụng một cách cẩn thận

+0

Không phải điều đó làm thất vọng JIT? Đó là những thứ tốt cho phiên dịch nhưng không tốt cho JIT, vì về bản chất chúng là các trình biên dịch và cần xử lý đặc biệt cho điều này –

2

Scheme cho phép bạn làm điều đó.

(define (salute-english name) (display "Hello ") (display name)) 
(define (salute-french nom) (display "Salut ") (display nom)) 

Bây giờ bạn xác định lại một fonction bằng cách gán biến salute đến chức năng đúng, hãy salute-english hoặc salute-french, như thế này:

(define salute salute-english) 

(define (redefined-the-salute-function language) 
    (if (eq? language 'french) 
     (set! salute salute-french) 
     (set! salute salute-english))) 

Nhiều ngôn ngữ lập trình generaly chức năng cho phép bạn làm điều đó hoặc như các chức năng là giá trị hạng nhất. Các hàm có thể được thao tác, được truyền xung quanh, đôi khi được gán cho các biến và cứ thế. Danh sách này sau đó bao gồm: Lisp, Scheme, Dylan, OCamlSML. Một số ngôn ngữ có chức năng lớp học đầu tiên bao gồm Python, Ruby, Smalltalk và tôi nghĩ Perl.Lưu ý rằng khi bạn có một ngôn ngữ tương tác, nơi bạn có thể tương tác gõ chương trình của bạn, việc xác định lại các hàm/phương thức phải có thể: REPL phải có khả năng thực hiện điều đó, chỉ trong trường hợp bạn nhập lại định nghĩa của một hàm đã được xác định.

0

Trong PLSQL:

create or replace procedure test 
as 
begin 
execute immediate ' 
create or replace procedure test2 
as 
begin 
    null; 
end; 
'; 
end; 
/
1

Bạn có thể làm điều đó trong C++, nhưng nó sẽ không dễ dàng, an toàn, hoặc khuyến khích.

  1. Tạo văn bản của mã nguồn
  2. gọi trình biên dịch (ngã ba & exec) để xây dựng một thư viện động. Trong gcc, bạn có thể chuyển mã nguồn bạn muốn biên dịch vào đầu vào tiêu chuẩn, nó không phải nằm trong tệp.
  3. tải thư viện (LoadLibrary() trên cửa sổ, dlopen() trên linux)
  4. có được một con trỏ hàm để bất cứ chức năng bạn muốn (GetProcAddress() trên cửa sổ, dlsym() trên linux)
  5. Nếu bạn muốn để thay thế một hàm hiện có, nếu đó là một hàm ảo, bạn có thể sửa đổi bảng v để trỏ đến hàm mới (phần đó đặc biệt là một ý tưởng khủng khiếp đầy nguy hiểm). Vị trí của v-table hoặc định dạng của nó không phải là một phần của tiêu chuẩn C++, nhưng tất cả các toolchains tôi đã sử dụng đã nhất quán trong bản thân họ, vì vậy một khi bạn tìm ra cách họ làm điều đó, nó có thể sẽ không phá vỡ.
0

Đây là một cái gì đó khác trong Python (ngoài câu trả lời của luc), mà tôi không đề xuất, nhưng chỉ để hiển thị nó - có exec, có thể thực hiện một chuỗi mà bạn có thể xây dựng là mã bất kỳ.

I/O được hiển thị ở đây là từ phiên phiên dịch Python 2.5.2. Chỉ cần một số ví dụ đơn giản xây dựng chuỗi thực hiện từ chuỗi con (>>> là dấu nhắc thông dịch viên) ...

>>> def_string = 'def my_func' 
>>> param_string_1 = '():' 
>>> param_string_2 = '(x):' 
>>> do_string_1 = ' print "Do whatever."' 
>>> do_string_2 = ' print "Do something with", x' 
>>> do_string_3 = ' print "Do whatever else."' 
>>> do_string_4 = ' print "Do something else with", x' 
>>> def_1 = '\n'.join([def_string+param_string_1, do_string_1, do_string_3]) 
>>> print def_1 
def my_func(): 
    print "Do whatever." 
    print "Do whatever else." 
>>> exec def_1 
>>> my_func() 
Do whatever. 
Do whatever else. 
>>> def_2 = '\n'.join([def_string+param_string_2, do_string_2, do_string_4]) 
>>> print def_2 
def my_func(x): 
    print "Do something with", x 
    print "Do something else with", x 
>>> exec def_2 
>>> my_func('Tom Ritter') 
Do something with Tom Ritter 
Do something else with Tom Ritter 
>>> 
0

Trivial trong Ruby:

def hello_world; puts "oops"; end 
hello_world 
# oops 
def hello_world; puts "hello world"; end 
hello_world 
# hello world 

Tất nhiên rằng ví dụ là nhàm chán:

require "benchmark" 
# why oh _why 
class Object 
    def metaclass; class << self; self; end; end 
    def meta_eval &blk; metaclass.instance_eval &blk; end 
end 

class Turtle 
end 

def make_it_move(klass) 
    klass.send(:define_method, :move) { |distance| 
    puts "moving #{distance} meters" 
    sleep(0.1 * distance) 
    } 
end 

make_it_move(Turtle) 

turtle = Turtle.new 
turtle.move(1) 
# moving 1 meters 

def profile(instance, method) 
    instance.meta_eval do 
    m = instance_method(method) 
    define_method method do |*a| 
     puts "Benchmarking #{instance.class} #{method}" 
     puts Benchmark.measure { 
     m.bind(instance).call(*a) 
     } 
    end 
    end 
end 

profile(turtle, :move) 

turtle.move(10) 
# Benchmarking Turtle move 
# moving 10 meters 
# 0.000000 0.000000 0.000000 ( 1.000994) 


Turtle.new.move(3) 
# moving 3 meters 

Đoạn mã trên:

  1. Định nghĩa một lớp trống
  2. Thêm một phương pháp để nó
  3. Grabs một thể hiện
  4. Ngăn chặn phương pháp mà trên đó dụ chỉ
0

Thay đổi những gì một chức năng không được hỗ trợ trong rất nhiều ngôn ngữ và nó không phức tạp như bạn nghĩ. Trong các ngôn ngữ chức năng, các hàm là các giá trị và các tên hàm là các ký hiệu được ràng buộc với chúng như bất kỳ biến nào. Nếu ngôn ngữ cho phép bạn gán lại biểu tượng cho một hàm khác, điều này là tầm thường.

Tôi nghĩ rằng các tính năng thú vị hơn là khả năng lấy mã nguồn cho hàm (toString ở trên) và để tạo hàm mới từ chuỗi (eval trong trường hợp này).

Các vấn đề liên quan