1. 程式人生 > >StackOverflow 週報 - 高質量問題的問答(Java、Python)

StackOverflow 週報 - 高質量問題的問答(Java、Python)

這是 Stack Overflow 第三週週報,本週加入了 Python 的內容,原計劃兩篇 Java、兩篇 Python。但明天過節所以今天就先把週報發了,兩篇 Java、一篇 Python。公眾號「渡碼」為日更,歡迎關注。

DAY1. 使用隨機數列印"hello world"

今天我們看一個有意思的例子,看看下面的程式碼為什麼每次執行都能輸出 "hello world"。

public static String randomString(int i) {
    Random ran = new Random(i);
    StringBuilder sb = new StringBuilder();
    while (true) {
        int k = ran.nextInt(27);
        System.out.println(k);
        if (k == 0) {
            break;
        }
        sb.append((char)('`' + k));
    }
    return sb.toString();
}

我們不禁會想,生成隨機數不是隨機嗎,為什麼每次執行結果都一樣。

要解釋這個問題,我們需要了解 “偽隨機” 的概念。只要隨機數是由確定的演算法生成的,那就是偽隨機,也就是說它的生成看似隨機但是有一定規律的。而“真隨機”需要真實的隨機事件取得,所以計算機只能生成偽隨機數。

知道了“偽隨機”概念,今天的例子就好解釋了。在之前的文章中我們看過生成隨機數的程式碼,是根據當前種子(seed)通過特定的規則(演算法)生成隨機數並更新種子,因此 Random 生成的隨機數是“偽隨機”數。此外我們都知道演算法有個特性叫確定性,也就是說相同的輸入只能得出相同的輸出。對於生成隨機數這個演算法來說,每次呼叫只要初始種子(seed)不變,那麼一定會生成相同的隨機數。這就解釋了,為什麼每次執行生成的隨機數序列都是一樣的。

最後,生成的 int 型別隨機數 k,通過 (char)('`' + k) 這行程式碼轉成字元,這裡用到的是 ASCII 碼相關的知識,不再贅述。

今天通過這個簡單的例子瞭解了偽隨機的概念。歡迎交流,關注公眾號每天分享一個知識點。

原文地址

DAY2. Java 巢狀類的兩種形式

Java 中巢狀類有兩種形式,官方定義為:如果巢狀類為靜態的,則稱為靜態巢狀類,如果是非靜態的則稱為內部類。設計巢狀類有以下的好處:

  • 程式碼組織:如果我們定義的某個類只服務於當前類,而不會在其他名稱空間中使用,那麼將它定義在當前類的名稱空間中是明智的。就像成員變數,在面向物件程式設計思想下,並不是所有的成員都要定義為全域性的
  • 訪問許可權:巢狀類可以直接訪問外部類的成員,即便是 private 修飾的
  • 便利性:沒必要為每一個類建立一個檔案

下面看看這兩種類的寫法以及優勢。先看看靜態巢狀類:

class OuterClass {
     private static int a =10;
     class StaticNestedClass {
     }
}
// 用法
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();

靜態類初始化時除了類前面加“外部類."外,其他的跟使用一個普通類相同。並且在 nestedObject 物件上使用跟普通物件一樣。靜態類最大的優勢在於可以直接訪問外部類私有的靜態成員。

再看看內部類:

class OuterClass {
    private int a = 10;
    class InnerClass {
    }
}
// 用法
OuterClass outerObject = new outerObject();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();

與非靜態成員類似,InnerClass 都繫結在一個外部物件上。因此初始化 InnerClass 時,需要先建立 OuterClass 物件。內部類除了可以訪問外部類的靜態私有成員外,還可以直接訪問外部類的非靜態私有成員。當然,內部類使用時有個限制,不能宣告 static 成員。

原文地址

DAY3. 是 Python 的設計缺陷嗎

先看一個問題,猜猜下面程式碼的輸出結果是什麼?

def a():
    print("a executed")
    return []

def b(x=a()):
    x.append(5)
    print(x)

b()
b()
b()

輸出結果如下:

a executed
[5]
[5, 5]
[5, 5, 5]

可以看到,每次呼叫 b() 時程式碼的輸出都不一樣。並且,a 函式只執行了一次。因此,我們可以確定,b 函式的預設引數是在函式定義時僅被計算了一次,而不是函式每次呼叫都會計算。這個問題可以這樣看:Python 中函式也是一個物件而預設引數是物件的成員,所以,它會被儲存、更新。但你可能會想,這是不是 Python 的設計缺陷。答案是否定的,如今的 Python 如此流行,如果僅僅是設計缺陷這麼簡單的問題,那麼會很快被修復。那麼,你可能還會想是不是可以讓函式執行時再確定預設引數值,這樣就可以避免上面的問題了。然而這樣同樣會有問題,看下面的例子:

fruits = ("apples", "bananas", "loganberries")

def eat(food=fruits):
    print(food)

def some_random_function():
    global fruits
    fruits = ("blueberries", "mangos")

假設,全域性變數 fruits 被修改了, 然後我們呼叫 eat 時,她的預設引數值就跟之前的呼叫不一致了, 同樣會令我們疑惑。而 Python 的設計者認為這種情況給語言使用者造成的疑惑更大,因此他們採用第一種的設計方式,在函式定義時確定預設值。

其實這樣的問題不光 Python 存在,任何語言都存在, 我們下面看一個 Java的例子。

StringBuffer s = new StringBuffer("Hello World");
Map<StringBuffer,Integer> counts = new HashMap<StringBuffer,Integer>();
counts.put(s, 5);
s.append("!!!!");
for (Map.Entry<StringBuffer, Integer> entry : counts.entrySet()) {
    System.out.println(entry.getKey() + "\t" + entry.getValue());
}

我們寫入 counts 的字串是 "Hello World",而輸出是變成了 "Hello World!!!!"。這跟我們剛剛討論的 Python 的問題類似,這裡沒有對錯,無論使用什麼方式,都會有人提出不同意見。到底使用哪種方式是語言的設計者考慮的問題,而每種方式有什麼坑,我們作為語言的使用者應該提前瞭解且避免。

以上便是 Stack Overflow 的第二週週報,希望對你有用,後續會繼續更新,如果想看日更內容歡迎關注公眾號。

公眾號「渡碼」,分享更多高質量內容