2012-03-10 25 views
22

Trong Go, có cách nào so sánh hai con trỏ hàm phi nil để kiểm tra tính bình đẳng không? Tiêu chuẩn bình đẳng của tôi là con trỏ bình đẳng. Nếu không, có bất kỳ lý do cụ thể tại sao con trỏ bình đẳng không được phép?Làm thế nào để tôi so sánh hai hàm cho bình đẳng con trỏ trong Go hàng tuần mới nhất?

Tính đến bây giờ, nếu tôi cố gắng để làm điều này một cách thẳng thắn:

package main 

import "fmt" 

func SomeFun() { 
} 

func main() { 
    fmt.Println(SomeFun == SomeFun) 
} 

tôi nhận được

./func-pointers.go:12: invalid operation: SomeFun == SomeFun (func can only be compared to nil) 

Đó là sự hiểu biết của tôi rằng hành vi này đã được giới thiệu gần đây.


Tôi đã tìm thấy câu trả lời bằng cách sử dụng gói phản ánh; tuy nhiên Atom đề xuất bên dưới rằng điều này thực sự tạo ra hành vi không xác định. Xem bài đăng của Atom để biết thêm thông tin và giải pháp thay thế có thể có.

package main 

import "fmt" 
import "reflect" 

func SomeFun() { } 

func AnotherFun() { } 

func main() { 
    sf1 := reflect.ValueOf(SomeFun) 
    sf2 := reflect.ValueOf(SomeFun) 
    fmt.Println(sf1.Pointer() == sf2.Pointer()) 

    af1 := reflect.ValueOf(AnotherFun) 
    fmt.Println(sf1.Pointer() == af1.Pointer()) 
} 

Đầu ra:

true 
false 

Trả lời

35

Lưu ý rằng có sự khác biệt giữa bình đẳng và nhận dạng. Các toán tử ==!= trong Go1 so sánh các giá trị tương đương (trừ khi so sánh các kênh), không phải cho danh tính. Bởi vì các toán tử này đang cố gắng không phải để kết hợp bình đẳng và nhận dạng, Go1 phù hợp hơn so với trước Go1 về mặt này.

Tính bình đẳng của chức năng khác với nhận dạng chức năng.


Một lý do không cho phép ==!= về loại chức năng là hiệu suất.Ví dụ, sau khi đóng cửa không sử dụng bất kỳ biến từ môi trường xung quanh:

f := func(){fmt.Println("foo")} 

Không cho phép các so sánh các chức năng cho phép trình biên dịch để tạo ra một thực đơn cho việc đóng cửa, thay vì đòi hỏi thời gian chạy để tạo ra một kết thúc mới (trong thời gian chạy). Vì vậy, từ quan điểm hiệu suất, quyết định không cho phép so sánh chức năng là một quyết định tốt.


Liên quan đến việc sử dụng gói reflect để xác định chức năng nhận dạng, một mã như

func SomeFun() {} 
func AnotherFun() {} 

func main() { 
    sf1 := reflect.ValueOf(SomeFun) 
    sf2 := reflect.ValueOf(SomeFun) 
    fmt.Println(sf1.Pointer() == sf2.Pointer()) // Prints true 

    af1 := reflect.ValueOf(AnotherFun) 
    fmt.Println(sf1.Pointer() == af1.Pointer()) // Prints false 
} 

dựa vào hành vi undefined. Không có đảm bảo về những gì chương trình sẽ in. Trình biên dịch có thể quyết định rằng nó sẽ hợp nhất SomeFunAnotherFun vào một triển khai duy nhất, trong trường hợp đó, lệnh in thứ 2 sẽ in true. Trong thực tế, hoàn toàn không đảm bảo rằng bản in đầu tiên sẽ in true (có thể, theo một số trình biên dịch Go1 khác và thời gian chạy, in false).


Một câu trả lời đúng cho câu hỏi ban đầu của bạn là:

package main 

import "fmt" 

func F1() {} 
func F2() {} 

var F1_ID = F1 // Create a *unique* variable for F1 
var F2_ID = F2 // Create a *unique* variable for F2 

func main() { 
    f1 := &F1_ID // Take the address of F1_ID 
    f2 := &F2_ID // Take the address of F2_ID 

    // Compare pointers 
    fmt.Println(f1 == f1) // Prints true 
    fmt.Println(f1 == f2) // Prints false 
} 
+1

Phản hồi tuyệt vời. Cảm ơn bạn! Bạn chắc chắn nhận được tín dụng để trả lời "tại sao", nhưng tôi nhầm lẫn về câu trả lời của bạn cho câu hỏi ban đầu của tôi. Có vẻ như nó đang kiểm tra danh tính của các biến F1_ID và F2_ID chứ không phải là danh tính của các hàm F1 và F2. Ví dụ, nếu tôi đã có 'var F1_ID2 = F1', sau đó & F1_ID == & F1_ID2 sẽ trả về true nếu chúng tôi đã kiểm tra nhận dạng chức năng; nhưng nó trả về sai. – BurntSushi5

+0

Ngoài ra, phê phán của bạn về cách tiếp cận của tôi bằng cách sử dụng phản ánh lo lắng cho tôi. Bạn không gợi ý rằng bản sắc chức năng kiểm tra là không thể đảm bảo trong Go? – BurntSushi5

+1

Comment1: Giả định trong đoạn mã cuối cùng là một hàm cụ thể có một ID duy nhất. –

1

weekly.2011-11-18

Bản đồ và giá trị hàm so sánh hiện đang không được phép (trừ so với con số không) theo kế hoạch Go 1. Chức năng bình đẳng là có vấn đề trong một số ngữ cảnh và bản đồ so sánh con trỏ, chứ không phải là nội dung của bản đồ.

Equality

bình đẳng Chức năng là vấn đề trong sự hiện diện của đóng cửa (khi hai đóng cửa bình đẳng?)

+3

Tôi không nghĩ rằng điều này thực sự trả lời câu hỏi của tôi. Tôi biết rằng so sánh giá trị hàm không được phép theo cách tôi đang sử dụng; những gì tôi muốn biết là nếu có bất kỳ cách giải quyết nào. Tôi cũng muốn biết tại sao chức năng * pointer * bình đẳng là vấn đề. – BurntSushi5

+1

@ BurntSushi5 Đây là một câu hỏi hay. Chắc chắn có những ngôn ngữ có đóng cửa, nơi bạn có thể so sánh con trỏ hàm. Các đóng * của cùng chức năng * được đóng trên các biến khác nhau không kiểm tra bằng các ngôn ngữ đó. Có vẻ như đây chỉ là một vấn đề đại diện. Không có lý do cơ bản cơ bản tại sao điều này không được phép trong Go. Nó ** có thể ** được thực hiện một cách hợp lý, đáng tin cậy và nhất quán; họ đơn giản chọn không làm như vậy. – tchrist

+0

@peterSO Để phản hồi chỉnh sửa của bạn: Điều đó không thực sự giải đáp được lý do tại sao hàm con trỏ bình đẳng không thể được sử dụng. Nếu hai con trỏ hàm trỏ đến cùng một vị trí bộ nhớ, thì chúng sẽ bằng nhau. Nó sẽ không hoàn toàn hữu ích như một hình thức bình đẳng chức năng chung, nhưng cũng sẽ không vô dụng. – BurntSushi5

3

Cách giải quyết phụ thuộc vào situtation. Tôi đã phải thay đổi một vài nơi mà tôi đã so sánh các chức năng. Trong trường hợp một lần tôi chỉ làm một cái gì đó khác nhau vì vậy tôi sẽ không cần phải so sánh chúng nữa. Trong một trường hợp khác tôi đã sử dụng một cấu trúc liên kết các chức năng với chuỗi so sánh, một cái gì đó như thế nào,

type nameFunc struct { 
    name string 
    fval func() 
} 

Tôi chỉ đã một vài chức năng tôi cần phải so sánh vì vậy nó là đơn giản nhất để giữ một lát những cấu trúc và quét lát khi cần, so sánh trường tên và gửi fval. Nếu bạn có rất nhiều, bạn có thể sử dụng bản đồ để thay thế. Nếu các hàm của bạn có các chữ ký khác nhau, bạn có thể sử dụng các giao diện, v.v.

+0

Ah tốt đẹp. Tôi cũng đã làm việc xung quanh nó bằng cách thay đổi cách tiếp cận của tôi. Cảm ơn bạn đã tip về việc giữ một biểu diễn chuỗi của một hàm. Nó sẽ không hoạt động tốt cho những gì tôi đang làm (cho phép các chức năng gọi lại tùy ý trong một thư viện), nhưng nó có thể hữu ích trong dòng. – BurntSushi5

+0

Gói phản ánh có những gì tôi đang tìm kiếm. Tôi đã cập nhật OP của tôi với câu trả lời. – BurntSushi5

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