2009-06-17 13 views
10

Trong khi chơi đùa với D 2.0 tôi thấy vấn đề sau đây:Làm thế nào để sử dụng tinh khiết trong D 2.0

Ví dụ 1:

pure string[] run1() 
{ 
    string[] msg; 
    msg ~= "Test"; 
    msg ~= "this."; 
    return msg; 
} 

này biên dịch và làm việc như mong đợi.

Khi tôi cố gắng quấn mảng chuỗi trong một lớp học tôi thấy tôi không thể có được điều này để làm việc:

class TestPure 
{ 
    string[] msg; 
    void addMsg(string s) 
    { 
     msg ~= s; 
    } 
}; 

pure TestPure run2() 
{ 
    TestPure t = new TestPure(); 
    t.addMsg("Test"); 
    t.addMsg("this."); 
    return t; 
} 

Mã này sẽ không biên dịch bởi vì chức năng addMsg là không tinh khiết. Tôi không thể làm cho hàm đó thuần khiết vì nó làm thay đổi đối tượng TestPure. Tôi có thiếu gì đó không? Hay đây là một hạn chế?

Sau đây không biên dịch:

pure TestPure run3() 
{ 
    TestPure t = new TestPure(); 
    t.msg ~= "Test"; 
    t.msg ~= "this."; 
    return t; 
} 

có các ~ = điều hành chưa được thực hiện như một hàm bất tịnh của mảng msg? Làm thế nào đến trình biên dịch không phàn nàn về điều đó trong hàm run1?

+0

Tôi đã cố gắng xóa thẻ [thẻ: thuần], vì đôi khi nó đề cập đến các hàm ảo thuần túy, đôi khi là [thuần] (http://beebole.com/pure/) và đôi khi thành [pure] (http://en.wikipedia.org/wiki/Pure_ (programming_language)) - trong số những thứ khác. Nhưng tôi không biết gì về [tag: d2]. Bạn có thể xác nhận xem chỉnh sửa thẻ của tôi có phù hợp không? [Tag: hoàn toàn chức năng] có hoạt động cho câu hỏi này không - tôi đã tạo [tag: pure-function], vì vậy nếu [tag: purely-functional] hoạt động, tôi nghĩ sẽ tốt hơn nếu sử dụng thẻ hiện tại. –

Trả lời

6

Kể từ v2.050, D giải thích định nghĩa của pure để chấp nhận hàm gọi là "yếu thuần túy". Điều này đề cập đến các chức năng "do not read or write any global mutable state". Các hàm kém tinh khiết là không giống như hàm thuần túy theo nghĩa ngôn ngữ chức năng. Mối quan hệ duy nhất là chúng tạo ra các hàm thuần túy thực, các hàm a.k.a. "mạnh thuần túy" có thể gọi các hàm yếu, như ví dụ của OP.

Với điều này, addMsgthể được đánh dấu là (yếu) pure, vì chỉ có các biến địa phương this.msg bị thay đổi:

class TestPure 
{ 
    string[] msg; 
    pure void addMsg(string s) 
    { 
     msg ~= s; 
    } 
}; 

và dĩ nhiên, bây giờ bạn có thể sử dụng (mạnh) pure chức năng run2 không có sửa đổi.

pure TestPure run2() 
{ 
    TestPure t = new TestPure(); 
    t.addMsg("Test"); 
    t.addMsg("this."); 
    return t; 
} 
+1

Rất tuyệt. Vui như thế nào không có cách tốt để đối phó với câu trả lời mà có được trong ngày trong ngăn xếp tràn. –

+0

chờ đợi, các hàm thuần túy yếu được phép sửa đổi các biến cá thể lớp? Trong trường hợp này, không phải là msg trong phạm vi của hàm "addMsg" và do đó không thể sửa đổi được? Nếu điều này là có thể, thì có gì sai khi viết một hàm thuần hoặc ủy nhiệm bên trong một hàm khác, điều đó sửa đổi trạng thái của hàm đó? –

+0

@Andrew: Không nằm ngoài phạm vi. Có một tham số 'this' ẩn trong các hàm thành viên và' msg' có thể truy cập từ 'this'. – kennytm

3

Vui lòng xem lại quy định chức năng tinh khiết:

chức năng tinh khiết là chức năng mà tạo ra kết quả tương tự cho các đối số tương tự. Cuối cùng, một chức năng tinh khiết:

  • có thông số đều bất biến hoặc là mặc nhiên chuyển đổi thành bất biến
  • không đọc hoặc viết bất kỳ nhà nước có thể thay đổi toàn cầu

Một trong những tác sử dụng các hàm thuần túy là chúng có thể được song song một cách an toàn. Tuy nhiên, nó không an toàn để thực hiện một số trường hợp của hàm của bạn song song, vì chúng có thể vừa sửa đổi thể hiện lớp đồng thời, gây ra một vấn đề đồng bộ hóa.

+0

"chúng có thể thay đổi cả hai thể hiện lớp cùng một lúc" .. what ?? làm sao? – hasen

+0

auto tp = new TestPure; void threadFunc() {tp.addMsg ("Xin chào thế giới"!); } Chủ đề mới (& threadFunc); Chủ đề mới (& threadFunc); Không thể đảm bảo rằng hai luồng thực hiện threadFunc sẽ không cố gắng ghi vào cá thể tp cùng một lúc. Do đó, addMsg không thể thuần khiết. –

+0

Ugh ... Trên bình luận ngoại trừ không bị hỏng với nhau: http://dump.thecybershadow.net/ca761137c80da1cee3f2657b215f758d/00000039.txt –

-1

Chỉ là linh cảm, nhưng hàm này không phải lúc nào cũng trả về cùng một kết quả.

Xem, nó trả về một tham chiếu đến một số đối tượng, và trong khi đối tượng sẽ luôn chứa cùng một dữ liệu, các đối tượng được trả về bởi nhiều cuộc gọi đến cùng chức năng không giống nhau; nghĩa là, họ không có cùng địa chỉ bộ nhớ.

Khi bạn trả về một tham chiếu đến đối tượng, về cơ bản bạn sẽ trả về một địa chỉ bộ nhớ, địa chỉ này sẽ khác nhau giữa nhiều cuộc gọi. Một cách khác để suy nghĩ về nó, một phần của giá trị trả về là địa chỉ bộ nhớ của một đối tượng, phụ thuộc vào một số trạng thái toàn cầu, và nếu đầu ra của một hàm phụ thuộc vào trạng thái toàn cục, thì nó không phải là nguyên chất. Địa ngục, nó thậm chí không phải phụ thuộc vào nó; miễn là hàm đọc trạng thái toàn cầu, thì nó không thuần khiết. Bằng cách gọi "mới", bạn đang đọc trạng thái toàn cầu.

+0

Logic của bạn ngụ ý rằng các hàm thuần túy chỉ có thể trả về các kiểu giá trị có thể phù hợp trong thanh ghi của bộ xử lý ... –

+0

Phần sau đây biên dịch và cho thấy các đối tượng khác nhau được trả về cho mỗi cuộc gọi. int thuần túy * test_pure2() { int * p = new int; * p = 42; return p; } int * i1 = test_pure2(); int * i2 = test_pure2(); writeln (i1, i2); –

+0

Hàm thuần túy sẽ trả về các phiên bản khác nhau có cùng giá trị. Rõ ràng là các con trỏ không thể giống hệt nhau trong hai trường hợp. Tôi nghĩ rằng nó rõ ràng những gì có nghĩa là ở đây - bạn không phải sử dụng địa chỉ của kết quả (nếu không - xem bình luận của tôi ở trên). –

0

I nghĩ rằng mã của bạn là chính xác về khái niệm.Tuy nhiên, bạn có thể đã tìm thấy trường hợp phân tích ngữ nghĩa của trình biên dịch không tốt bằng bộ não của bạn.

Xem xét trường hợp nguồn của lớp không có sẵn. Trong trường hợp đó trình biên dịch sẽ không có cách nào để nói rằng addMsg chỉ sửa đổi biến thành viên để nó không thể cho phép bạn gọi nó từ một hàm thuần túy.

Để cho phép trong trường hợp của bạn, nó sẽ phải có xử lý trường hợp đặc biệt cho loại sử dụng này. Mỗi quy tắc đặc biệt được thêm vào làm cho ngôn ngữ trở nên phức tạp hơn (hoặc, nếu không có giấy tờ, làm cho nó ít cầm tay hơn)

4

Những người khác đã chỉ ra rằng addMsg không thuần khiết và không thể thuần khiết vì nó làm thay đổi trạng thái của đối tượng.

Cách duy nhất để làm cho nó tinh khiết là gói gọn những thay đổi bạn đang thực hiện. Cách dễ nhất để làm điều này là thông qua đột biến trả về và có hai cách để thực hiện việc này.

Thứ nhất, bạn có thể làm điều đó như thế này:

class TestPure 
{ 
    string[] msg; 
    pure TestPure addMsg(string s) 
    { 
     auto r = new TestPure; 
     r.msg = this.msg.dup; 
     r.msg ~= s; 
     return r; 
    } 
} 

Bạn cần phải sao chép các mảng trước vì bên trong một hàm thuần túy, các thông tin này thực sự là const. Lưu ý rằng bạn có thể làm bản sao tốt hơn bằng cách phân bổ một mảng mới của kích thước cuối cùng và sau đó sao chép các phần tử trong chính bạn. Bạn sẽ sử dụng chức năng này như sau:

pure TestPure run3() 
{ 
    auto t = new TestPure; 
    t = t.addMsg("Test"); 
    t = t.addMsg("this."); 
    return t; 
} 

Bằng cách này, đột biến được giới hạn trong mỗi hàm thuần với các thay đổi được chuyển qua giá trị trả về.

Một cách thay thế các văn bản TestPure sẽ làm cho các thành viên const và làm tất cả những đột biến trước khi đi qua nó để các nhà xây dựng:

class TestPure 
{ 
    const(string[]) msg; 
    this() 
    { 
     msg = null; 
    } 
    this(const(string[]) msg) 
    { 
     this.msg = msg; 
    } 
    pure TestPure addMsg(string s) 
    { 
     return new TestPure(this.msg ~ s); 
    } 
} 

Hy vọng rằng sẽ giúp.

+0

Tôi đoán đây có lẽ là điều tốt nhất. Tôi chỉ hy vọng chúng ta có thể tránh tạo ra tất cả rác thải và dành thời gian để tạo ra các bản sao bằng cách nào đó. –

+2

Thật buồn cười khi bạn nói rằng: hầu hết các ngôn ngữ chức năng tạo ra một lượng lớn rác thải cho chính xác cùng một lý do. Đó là lý do tại sao người thu gom rác của họ có xu hướng rất hiệu quả. Đây không phải là điều dễ giải quyết. Nếu bạn cần phải biến đổi, không sử dụng tinh khiết. –

+0

BTW, điều này không còn đúng nữa. – BCS