計算機界 TOP 3難題:「相等」是軟體工程中許多重大問題的根源!

2020-12-12 CSDN

有一個笑話說,計算機科學界有兩大難題:一是緩存失效問題,二是命名問題。但我認為還有第三個更難的問題:相等問題。你沒看錯,等號「=」看似簡單,但等號的使用和誤用,是軟體工程中許多重大問題的根源。聲明:本文已獲作者Craig Stuntz翻譯授權。

作者 | Craig Stuntz

譯者 | 彎月,責編 | 郭芮

頭圖 | CSDN 下載自視覺中國

出品 | CSDN(ID:CSDNnews)

以下為譯文:

相等的原理

我來展示一下在程式語言中相等有多麼容易出錯。但首先我要解釋一下相等應有的模樣,而這其實非常難解釋!當我們討論相等「應該如何」時,我們必須指出是特定語境下的相等,因為相等的成立有許多方式,許多方式在不同的語境下都是成立的。

「數學的精髓在於,同一個東西可以用不同的方式呈現給我們。」——Barry Mazur,《When is one thing equal to some other thing》

定律

我說過,在不同的語境下,相等有不同的含義,但儘管如此,有些東西是永遠成立的。這就是相等的定律。

相等是一個二元操作,它是:

反射的,即對於任意值 a 都有 a = a對稱的,即a = b可以推出b = a,反之亦然傳遞的,即如果a = b且b = c,則a = c在編程的世界中,我們需要增加一條定律,因為有時候程式設計師會做一些奇怪的事情:

相等必須是:

一致的,即如果a = b且a或b的任何欄位都沒有發生變化,那麼稍後再次檢查時a = b應當依然成立。上面的定律看似簡單,但流行的程式語言甚至連如此簡單的定律都無法遵守。但更嚴重的關於相等的問題甚至都很難精確描述。

結構相等性

在程式語言對於相等的各種實現中,一個重要的區別就是結構相等和引用相等。

結構相等會檢查兩個引用是否為同一個值。F#默認採用了結構相等:

type MyString = { SomeField : string }

let a = { SomeField = "Some value" }

let b = { SomeField = "Some value" }

if a = b then // 返回true, 進入 "then" 代碼塊

但在C#中則不是這樣,C#使用的是引用相等。引用相等要求兩個被比較的對象是同一個對象。換句話說,它會比較兩個變量是否指向同一個內存地址。指向兩個不同內存地址的引用會被判為不相等,即使它們的值完全一樣:

class MyString {

private readonly string someField;

public string SomeField { get; }

public MyString(string someField) => this.someField = someField;

}

var a = new MyString("Some value");

var b = new MyString("Some value");

if (a == b) { // 返回 false, 不會進入代碼塊

其他語言會讓你選擇。例如,Scheme提供了equal?來檢查結構相等,eq?來檢查引用相等。Kotlin提供了==用於結構相等,===用於引用相等(不要與JavaScript的==和===操作符混淆,JavaScript的那個是完全不同的東西)。

程序中何時應當使用結構相等?如果變量的值不會改變更,那麼幾乎任何情況下都應該使用結構相等!我所知的絕大多數程式語言在諸如integers之類的類型上都使用結構比較。除了Java之外,int類型進行結構比較,Integer類型進行引用比較,這迷惑了一代又一代的程式設計師。Python的is也有類似的問題。

對於引用類型(如對象)也應當進行結構比較。考慮一個單元測試,你希望檢查返回的對象是否等於你期待的值。在使用結構相等的語言中,這個操作非常簡單:

[<TestMethod>]

let ``The result of the calculation is the expected value``() =

let expected = { SomeField = "Some value"; SomeOtherField = 15; StillAnotherField = true; ... }

let actual = calculate()

Assert.AreEqual(expected, actual)

但如果語言不支持結構相等,而開發者需要自行開發,就會遇到難題。

引用相等

但正如我剛才說過的那樣,某些特定情況下不應該使用結構相等。一種情況就是語言支持變量內容改變的情況,而絕大多數程式語言都支持。當某個變量的值被改變後,說這個變量等於另一個變量顯然是不合理的。當然,你可以說在進行比較的時刻,這兩個變量(在結構上)是相等的,比如在單元測試的最後一行時是相等的,但一般情況下你無法假設這兩個變量是同一個東西。這點理解起來有些困難,我來舉例說明。

我們假設有一個對象,表示一個人。在採用了結構相等的F#中,我可以這樣寫:

type Person = { Name : string; Age : integer; Offspring : Person list }

現在我有兩個朋友Jane和Sue,她們都有一個叫John的兒子,年齡都是15歲。他們是不同的人,但姓名和年齡都一樣。沒問題!

let jane = { Name = "Jane"; Age = 47; Offspring = [ { Name = "John"; Age = 15; Offspring = [] } ] }

let sue = { Name = "Sue"; Age = 35; Offspring = [ { Name = "John"; Age = 15; Offspring = [] } ] }

也可以這樣寫:

let john = { Name = "John"; Age = 15; Offspring = [] };

let jane = { Name = "Jane"; Age = 47; Offspring = [ john ] }

let sue = { Name = "Sue"; Age = 35; Offspring = [ john ] }

這兩段代碼的功能完全一樣。我沒辦法區別兩個兒子,即使我知道他們是不同的人。但這沒有問題!如果我需要區別他們,我可以把他們DNA的hash之類的屬性加到Person類型中。但如果我只需要知道他們的名字和年齡,那麼是否能區分兩個對象並不重要,因為不管怎麼區分,它們的值都是一樣的。

假設Jane的兒子改名成Pat。F#不支持改變變量的值,所以我需要為John(還有Jane!)創建新的Person實例:

let newJane = { Name = "Jane"; Age = 47; Offspring = [ { Name = "Pat"; Age = 15; Offspring = [] } ] }

這個新的變量newJane似乎有點奇怪,但實際上並不會構成問題。上面的代碼沒有問題。現在用C#試一下,在C#中,變量默認情況下是可以修改的:

var john = new Person("John", 15, null);

var jane = new Person("Jane", 15, new List<Person> { john });

var sue = new Person("Sue", 15, new List<Person> { john });

這段代碼顯然是不正確的:如果Jane的兒子改名為Pat,我可以直接改變引用的值:

jane.Offspring.First().Name = "Pat";

但我就會發現Sue的兒子也改名了!因此,即使兩個兒子最初的名字是一樣的,但他們並不相等!所以我應該寫成:

var jane = new Person("Jane", 15, new List<Person> { new Person("John", 15, null) });

var sue = new Person("Sue", 15, new List<Person> { new Person("John", 15, null) });

這樣Jane和Sue的孩子就是引用不相等。所以,在可以改變變量內容的語言中,默認採用引用相等是合理的。

另一種應該採用引用相等的情況是,事先知道引用相等的結果與結構相等相同。測試結構相等顯然需要額外開銷,如果真的需要測試結構相等,那麼這個額外開銷是正常的。但是,假設你創建了大量的對象,而且事先知道每個對象都是結構不相等的,那麼花費額外開銷來測試結構相等是沒有必要的,因為僅僅測試引用相等就可以得出同樣的結果。

相等性的表示

在實數中,0.999……(無限循環小數)等於1。注意這裡說的「實數」與程式語言中的Real類型不一樣。在數學中,實數是無限的,而程式語言中的實數是有限的。因此,程式語言中沒有0.999……這樣的寫法,但沒關係,你可以使用1,反正兩者的值是一樣的。

這本質上是數學家在表示實數系統時採用的一種選擇。如果在系統中加入另外一種對象,比如無限小的數,那麼0.999……和1就不相等了。

「但是這並不等於說規範可以任意確定,因為不接受一種規範,必然會導致不得不發明奇怪的新對象,或者不得不放棄某些熟知的數學規則。」——Timothy Gowers,《Mathmetics: A Very Short Introduction》

類似地,在實數系統中,1/2和2/4表示同樣的值。

不要把這些「相等」與JavaScript或PHP中的「不嚴格」相等運算符==混淆。這些相等跟那些運算符不一樣,這些相等依然遵循相等的定律。重要的是要認識到,對象的相等可以用不同的方式來表達。

在IEEE-754浮點數系統中,-0 = 0。

內涵和外延

一個函數何時等於另一個函數?絕大多數程式語言會進行引用相等的比較,我覺得這沒有問題。因為,對函數進行結構比較有什麼意義呢?也許我們可以使用反射來檢查函數的實現,看看它們實現是否一樣?但怎樣才叫「一樣」?變量名是否必須完全一樣?快速排序和歸併排序是不是「一樣」的函數?

因此我們說,只要函數對於同樣的輸入返回同樣的輸出(不管其內部實現如何),函數就是外延相等的,而如果內部定義也一樣,則是內涵相等的。當然,這也取決於語境。可能在某個語境中,我需要常數時間的函數,在另一個語境中,速度無關緊要。重要的是,必須有語境才能定義相等,才能用它來比較兩個函數。

我不知道是否有哪種語言在比較函數時嘗試過採用引用相等之外的方法。但很容易想出,這會很有用!(例如,優化器嘗試移除重複的代碼等。)你只能自己實現,但我不得不說,沒有相等比較,總要比錯誤的相等比較強。

相等和賦值

當程式設計師的第一天就學過,「等於」這個名字有兩種不同的概念。一種是賦值,另一種是測試相等性。在JavaScript中需要這樣寫:

const aValue = someFunction(); // 賦值

if (aValue === 3) { // 測試相等

這兩者本質上是不同的。比較返回布爾值,而在面向表達式的語言(如Ruby)中,賦值返回被賦的值。

所以Ruby代碼可以這樣寫:

a = b = c = 3

實際上會把3賦給變量a,b和c。不要在引用類型上嘗試,很可能得不到你想要的結果!

在非面向表達式的語言(如C#)中,賦值沒有返回值。

在數學中,賦值和測試相等性都使用相等運算符:

if aValue = 3 ...

where aValue = someFunction()

(而且在數學中,有時候=還用於其他關係,如合同(congruence)。與數學中的其他東西一樣,這裡也要區分語境;在閱讀論文或書籍時必須注意語境。)

為什麼數學不要求兩種不同的操作,而程式語言要求?因為在數學中可以輕易判斷出語境,而且也並非所有語言都要求不同的運算符。例如,F#中賦值和測試相等都採用=。儘管兩者採用相同的符號,但賦值和測試相等是完全不同的操作。

let aValue = someFunction(); // 賦值

if aValue = 3 then // 測試相等

語法的選擇部分出於歷史原因:F#基於ML,而ML基於數學;而JavaScript的語法基於Java→C→Algo→FORTRAN。

用於編譯FORTRAN代碼的機器很難根據語法來區分兩種情況,因此採用不同的運算符是合理的。於是C語言把這個「特性」帶到了新的高度,所以你甚至可以寫:

int aValue = someFunction(); // 賦值

if (aValue = 3) { // 也是賦值!

給沒有C語言經驗的人解釋一下:這段代碼先用3覆蓋aValue,然後由於表達式aValue = 3的值為3,因此if的條件為真,因此會繼續執行if塊內的代碼。通常這種寫法都是錯誤的,因此許多C程式設計師會將if塊的條件反過來寫,來避免造成該錯誤:

int aValue = someFunction(); // 賦值

if (3 == aValue) { // 測試相等

// [...]

if (3 = aValue) { // 語法錯誤:無法將 aValue 賦值給 3.

相等性的使用錯誤

通過上面的說明,希望大家都已經明白相等性並不簡單,「正確」的實現取決於語境。儘管如此,程式語言經常會把最容易的地方搞錯!很多時候,這是相等性與其他語言特性的組合造成的,如隱式類型轉換。

常見錯誤:相等性不是反射的

回憶一下相等性的反射率,即任何值都等於它自身,a = a。

在.NET中,如果在值類型上調用Object.ReferenceEquals(),其參數會在執行方法之前分別進行打包,因此即使傳遞同一個實例,也會返回假:

(來自文檔的例子)

int int1 = 3;

Console.WriteLine(Object.ReferenceEquals(int1, int1)); // 輸出 False

這意味著在任何.NET語言中 a = a 都不一定為真,因此不滿足反射率。

在SQL中,NULL不等於自身,因此表達式NULL = NULL(或者更可能的情況是,SOME_EXPRESSION = SOME_OTHER_EXPRESSION時兩者都可能為null)會返回false。這會導致下面亂糟糟的語句:

WHERE (SOME_EXPRESSION = SOME_OTHER_EXPRESSION)

OR (SOME_EXPRESSION IS NULL AND SOME_OTHER_EXPRESSION IS NULL)

而更可能發生的情況是,開發者會忘記NULL的特殊規則從而導致bug。一些資料庫伺服器的SQL語言支持IS NOT DISTINCT FROM,它的功能才是=應該有的功能。(或者我應該說,它沒有不做=應該做的事情?)否則,就必須使用上面例子中的SQL語句。最好的解決辦法就是儘可能使用不允許NULL的列。

IEEE-754浮點數也有同樣的問題,即NaN != NaN。一種解釋是,NaN表示某個不確定的「非數字」結果,而不同計算得出的NaN並不一定是同一個不確定的非數字,所以這個比較本身就是不正確的。例如,square_root(-2)和infinity/infinity兩者的結果都是NaN,但顯然它們不一樣!有時候SQL的NULL問題也可以類似地解釋。這樣造成的問題之一就是術語的含義過多:NaN和NULL表示的是「未知」,還是「不精確的值」,或者是「缺少值」?

對於此類正常的浮點運算中不會出現的問題,解決方法之一就是採用聯合(union)類型。在F#中可以這樣寫:

type MaybeFloat =

| Float of float

| Imaginary of real: float * imaginary: float

| Indeterminate

| /// ...

然後就可以在計算中正確處理這些情況了。如果在計算中遇到預料之外的NaN,可以使用signaling NaN來拋出異常。

Rust提供了Eq和PartialEq兩個trait。沒有實現Eq,是==運算符不遵從反射率的一個信號,而Rust中的浮點類型就沒有實現Eq。但即使不實現Eq,你依然可以在代碼中使用==。實現Eq可以將對象作為hash map的鍵使用,可能會導致其他地方的行為發生變化。

但是=和浮點數還有更嚴重的問題。

常見錯誤:相等過於精確

我想許多開發者都熟悉IEEE-754浮點數的比較問題,因為絕大多數語言的「float」或「double」的實現都是IEEE-754。10 *(0.1) 不等於1,因為「0.1」實際上等於0.100000001490116119384765625,或0.1000000000000000055511151231257827021181583404541015625。如果你對此問題感到陌生,你可以閱讀這篇文章(https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/),但這裡的重點是,在浮點數上使用==進行比較是完全不安全的!你必須決定哪些數字是重要的,然後據此進行比較。

(更糟糕的是,浮點數是許多其他類型的基礎,如某些語言中的TDateTime類型,所以即使一些相等比較本該合理的地方,也不能正常工作。)

比較浮點數的正確方法是看它們是否「相近」,而「相近」在不同語境下有不同的含義。這並不是簡單的==能夠完成的。如果你發現經常需要做這種事情,那麼也許你該考慮使用其他數據類型,如固定精度的小數。

既然如此,為什麼程式語言要在無法支持的類型上提供==比較呢?其實程式語言為每一種類型都提供了==,程式設計師需要依靠自己的知識來判斷哪些不能用。

SML的實現說明(http://sml-family.org/Basis/real.html)上這樣說:

判斷real是否為相等的類型,如果是,那麼相等本身的意義也是有問題的。IEEE指出,零的符號在比較中應當被忽略,而任意一個參數為NaN時,相等比較應當返回false。這些約束對於SML程式設計師來說非常麻煩。前者意味著 0 = ~0 為true,而r/0 = r/~0為false。後者意味著r = r可能出現返回false的異常情況,或者對於ref cell rr,可能存在 rr = rr 成立但是 !rr = !rr 不成立的情況。我們可以接受零的無符號比較,但是認為相等的反射率、結構相等,以及<>和not o =的等價性應當被保留。這些額外的複雜性讓我們作出決定,real不是具有相等性的類型。

通過禁止real擁有=運算,SML強迫開發者思考他們真正需要什麼樣的比較。我認為這個特性非常好!

F#提供了[<NoEquality>]屬性,來標記那些=不應該被使用的自定義類型。遺憾的是,他們並沒有將float做上標記!

常見錯誤:不相等的「相等」

PHP有兩個單獨的運算符:==和===。==的文檔將其稱為「相等」,並記載到「如果在類型轉換後$a等於$b則返回TRUE」。不幸的是,這意味著==運算符是不可靠的:

<?php

var_dump("608E-4234" == "272E-3063"); // true

?>

儘管這裡比較的是字符串,但PHP發現兩者都可以被轉換為數字,所以就進行了轉換。由於這兩個數字非常小(例如第一個數字是608 * 10^-4234),而我們之前說過,浮點數比較非常困難。將這兩者都轉換成浮點數float(0)將導致它們被四捨五入成同一個值,因此該比較返回真。

注意這與JavaScript的行為不同。JavaScript也有與PHP類似的(但並不是一樣的!)==和===運算符;但JavaScript會認為兩側都為字符串,然後返回比較結果false。

幸運的是,PHP提供了===(「全等」)運算符,在這種情況下能給出正確結果。我想說永遠不要使用==,但==會在對象上執行結構比較,有時候正是你需要的!因此我只能說,使用==時要格外小心,因為它不能在基礎類型上正確工作。

常見錯誤:相等不是對稱的

如果你要在Java中重載.equals(),那麼你必須負責確保相等的定律成立!

如果不加注意,那麼很容易就會導致不對稱的相等,即a.equals(b) != b.equals(a)。

即使不考慮null的情況(因為null會導致NullPointerException,而.equals()是允許這種情況發生的:https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#equals-java.lang.Object-),如果你繼承一個類並重載.equals(),也最好多加小心!

@Override

public boolean equals(Object o) {

if (this == o)

return true;

if (o == null)

return false;

if (!o.getClass().isAssignableFrom(getClass())) // 危險!這一步是錯的!

return false;

ThisClass thisClass = (ThisClass) o;

// 欄位比較

// ...

}

如果ThisClass和ASubtypeOfThisClass都用類似上面的代碼重載了.equals(),那麼a.equals(b)就可能不等於b.equals(a)!正確的比較應該是:

if (getClass() != o.getClass())

return false;

這不僅僅是我的個人看法,也是Object.equals()的契約的要求(https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#equals-java.lang.Object-)。

常見錯誤:相等沒有傳遞性

回憶一下相等比較的定律之一就是應當具有傳遞性,即如果a = b 且 b = c,那麼 a = c。不幸的是,與類型轉換(type coersion)放在一起後,許多語言都會在這裡出問題。

在JavaScript中,

'' == 0; // true

0 == '0'; // true

'' == '0'; // false!

因此在JavaScript中永遠不要使用==,應該使用===。

常見錯誤:相等性不一致

在Kotlin中,==會根據變量類型返回不同的值,即使對於同一個變量:

fun equalsFloat(a: Float, b: Float) {

println(a == b);

}

fun equalsAny(a: Any, b: Any) {

println(a == b);

}

fun main(args: Array<String>) {

val a = Float.NaN;

val b = Float.NaN;

equalsFloat(a, b);

equalsAny(a, b);

}

// prints false, true

這是一個非常不幸的語言特性組合,可能會導致違反直覺的行為。

常見錯誤:在應當使用結構相等的地方使用引用相等

考慮如下用C#編寫的MSTest單元測試:

[TestMethod]

public void Calculation_Is_Correct() {

var expected = new Result(SOME_EXPECTED_VALUE);

var actual = _service.DoCalculation(SOME_INPUT);

Assert.AreEqual(expected, actual);

}

這段代碼能正常工作嗎?我們不知道!Assert.AreEqual()最終會調用Object.Equals(),默認會進行引用比較。除非你重載了Result.Equals()進行結構比較,否則這個單元測試無法正常工作。Object.Equals()認為,如果類型是可改變的,那麼不應該重載。通常來說這是合理的,但在單元測試中卻未必。(這是因為.Equals()本應比較.GetHashCode(),而一個對象的hash code在對象的生命周期中應該不發生改變。).NET framework中對於引用類型的最接近「有保證的結構比較」的是IEquatable<T>,但Assert.AreEqual()並沒有使用,即使實現了也不會使用。

而NUnit的情況更糟(https://github.com/nunit/nunit/issues/1249)。

(相反,Java的Object.hashCode在對象的欄位發生變化時是允許變化的。https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#hashCode())

應該怎樣看待相等

沒想到關於=運算符我寫了這麼多還沒寫完!好吧,這已經遠遠超過了運算符本身。為什麼如此複雜?基本上有兩個原因:

非本質的複雜性:我們的程式語言在相等比較方面做得並不好。經常完全不能正常工作,甚至在不能正常工作時也不會明確表示這一點,例如會在本應進行引用比較的地方使用結構比較。本質的複雜性:相等性本身就是複雜的,如比較浮點數。而在諸如比較函數等邊緣情況下就更為複雜。另一種劃分方法就是「應該由程式語言的實現者負責解決的問題」(上面的「非本質的複雜性」)和「應該由程式語言的使用者負責解決的問題」。

程式語言應該怎麼做?

關於非本質的複雜性,現狀是幾乎每一種主流程式語言對於相等性的實現都有問題。這個「必須遵循幾個定律的簡單運算」正是程式語言為了保證正確性而依賴的東西!但在我看來,只有SML真正思考了怎樣在語義和運行時/標準庫方面同時保證符合定律的相等性,而SML完全不是主流語言。

首先,在禁止相等比較的地方,程式語言應該很容易創建不允許相等比較的類型,因為這易操作完全沒有必要複雜(如F#中的[<NoEquality>]),然後應該在標準庫中儘可能多地使用該特性,如浮點類型。

程式語言必須非常明確地指出結構相等和引用相等之間的差異。永遠都不應該存在行為不確定的情況。絕大多數程式語言會重載==,根據引用的類型(多數情況是根據值或引用的區別),用它來表示結構相等或引用相等,這樣做一定會讓開發者感到困惑。

Kotlin已經非常接近正確了,它的===表示引用相等,==表示結構相等,儘管出於某些原因,對於值類型它會將===看做==,而不是引發編譯錯誤。目標應該是減少開發者的困惑。它希望讓開發者明白,===表示「引用相等」,而不是等號越多越好。

我不知道還有哪些允許改變變量值的語言能夠用不困惑的方式處理結構相等的。但很容易想像理想狀態應該怎樣!準備兩個運算符,一個表示結構相等,一個表示引用相當,只在程式語言可以合理地支持的語境下才允許相應的運算符。例如,如果.NET的Object.ReferenceEquals和值類型不進行包裹,並且使用類似於IEquatable<T>的東西允許成功夠許願使用結構相等運算符,那麼開發者就很容易弄清楚哪個是哪個。

程式設計師應該怎麼做?

也許你讀了這篇文章後會覺得,「哇,相等好複雜!我還是不要編程了,回家種地算了。」但這篇文章如此之長的原因主要是太多的語言都做錯了。都作對的確需要些心思,但並不是太難。肯定比種地要簡單。

在已有的類型上進行相等比較時,先問問自己:

在這裡進行相等比較本身合理嗎?如果合理,那麼是應該進行結構比較,還是引用比較?對於相應的比較方法,我採用的程式語言提供了哪些支持?我採用的程式語言對於該比較方法的實現是正確的嗎?在設計自定義類型時也可以詢問類似的問題:

我的類型應該支持相等比較嗎?還是需要一個更複雜的比較,就像float那樣?我的類型應該是可改變的嗎?它會對相等性產生怎樣的影響?應該支持引用比較?還是結構比較?還是應該同時支持兩者?如果你的類型是可改變的,則應該考慮將其改成不可改變的。即使語言默認是可改變的,這一點也可以實現!這樣做除了能在相等性比較方面獲得許多好處之外,不可改變的架構還有許多其他的好處。採用了不可改變數據結構的C# Roslyn編譯器就是非常好的例子:

語法樹的第三個屬性是,它們是不可改變的,而且是線程安全的。這意味著,在獲得一棵樹之後,它就是當前代碼狀態的快照,而且永遠不會改變。這樣多個用戶可以在不同的線程中與同一個語法樹同時進行操作,而無需擔心死鎖或重複的問題。由於樹是不可改變的,也不能針對樹進行直接的改變,因此負責創建和修改語法樹的工廠方法實際上會創建樹的新快照。樹本身的效率很高,因為它會重用底層結點,所以創建新版本的速度很快,只需要使用少量內存。

——.NET Compiler Platform SDK文檔

原文:https://www.craigstuntz.com/posts/2020-03-09-equality-is-hard.html

相關焦點

  • 中科協發布今年20個重大科學問題和工程技術難題
    2019年20個對科學發展具有導向作用、對技術和產業創新具有關鍵作用的前沿重大科學問題和工程技術難題。  據中國科學院院士、中國科協名譽主席韓啟德介紹,所發布的這些重大科學問題和工程技術難題,既和當下的國計民生息息相關,又有面向未來的戰略意義。他認為從這些問題和難題中可以看出,我國的科技創新能力正從「量的積累」向「質的飛躍」轉變、從「點的突破」向「系統能力提升」轉變。他表示,期待未來能見證這些問題和難題的突破。
  • 2019年20個重大科學問題和工程技術難題發布
    科技日報哈爾濱6月30日電 (實習記者代小佩)30日,在第二十一屆中國科協年會閉幕式上,中國科協副主席、中國工程院院士周守為發布了2019年20個對科學發展具有導向作用、對技術和產業創新具有關鍵作用的前沿科學問題和工程技術難題。20個難題如下:暗物質是種能探測到的基本粒子嗎?
  • 科普:為什麼軟體有不同的分類,以及軟體在計算機中的必要性
    有的是為了解決某種專門的問題,如汽車與飛機的設計,高樓大廈的設計,服裝鞋帽的設計、發電站自動控制等等;有的則是為設計其他各種應用系統提供工具和支援。此外,由於所有在計算機上運行的軟體都要使用計算機的資源,所以就要有軟體來管理資源,協調軟、硬體的運行。軟體按功能和作用大致上可分成三大類:系統軟體、支撐軟體和應用軟體。
  • 中國科協發布2019年20個重大科學問題和工程技術難題
    6月30日,中國科協在第二十一屆中國科協年會閉幕式上發布了2019年20個對科學發展具有導向作用、對技術和產業創新具有關鍵作用的前沿科學問題和工程技術難題。「哪些科學問題和技術難題需要優先攻破?這些問題的發布能起到技術引領作用,有助於社會各界、一線科技工作者及企業家預判世界科技發展方向,集中精力攻克,帶來巨大經濟效益,形成新的產業和業態。」
  • 軟體工程經典問題大總結,每一個問題都是軟體工程的重要內容
    管理人員可把這些記載下來的材料作為檢查軟體開發進度和開發質量的依據,實現對軟體開發的工程管理。2)提高開發效率。軟體文檔的編制,使得開發人員對各個階段的工作都進行周密思考、全盤權衡、從而減少返工。並且可在開發早期發現錯誤和不一致性,便於及時加以糾正。3)作為開發人員在一定階段的工作成果和結束標誌。4)記錄開發過程中的有關信息,便於協調以後的軟體、開發、使用和維護。
  • 計算機科學、軟體工程、信息工程等計算機專業,哪個適合女生?
    事實上,標題具體提到的3個專業之間是沒有硬邊界的,相互之間的學習是很方便的。為什麼會這樣說呢?原因有二:一是計算機科學與技術、軟體工程、信息工程,三個專業從課程設計上,是絕對的近親關係,大一大二很多課都是一起上的,大三的專業課也很多共用老師。
  • 計算機top院校大盤點!
    計算機專業是現在最火的專業之一,而保研又是一個越來越卷的過程,但是每年還是會有不少外校的大佬保研到國內計算機頂級的top院校。今天島主就帶大家來看看國內計算機的top院校,我們也提前感受下計算機專業最高學府的風採~Ps:介紹順序不代表實力排名~國內Top2,很多人都評價是「計算機專業全國最強的學府」。
  • 2020重大科學問題和工程技術難題 入選題目簡介
    8月15日,在第二十二屆中國科協年會閉幕式上,中國科協重磅發布了2020「重大科學問題和工程技術難題」,受到科技界的廣泛關注。據了解,此次徵集發布活動共徵集到103家全國學會、學會聯合體、企業科協提交的490個問題難題,1.88萬餘名院士、專家、一線科技工作者參與評選。經過網絡初評投票、覆審評議和終審評議,最終選出10個對科學發展具有導向作用的科學問題和10個對技術和產業具有關鍵作用的工程難題。
  • 想成為高薪軟體工程師,該選計算機科學專業還是軟體工程專業?
    2019年的高考分數已經陸續公布,選擇什麼大學、報考什麼專業也將成為考生和家長要面對的難題。計算機類的專業是近些年的大熱門,但是每個專業之間具體學習內容和發展方向卻又有一些不同。計算機科學主要研究計算機及其周圍各種現象和規律的科學,即研究計算機系統結構、程序系統(即軟體)、人工智慧以及計算本身的性質和問題的學科。計算機科學是一門包含各種各樣與計算和信息處理相關主題的系統學科,從抽象的算法分析、形式化語法等等,到更具體的主題如程式語言、程序設計、軟體和硬體等。
  • 與計算機有關,軟體工程和計算機科學與技術,差別不是一般大
    軟體工程專業、計算機科學與技術專業,兩個不同的專業,全與計算機有關。許多人都認為這兩個專業很相似,其是差別還是挺大的。計算科學與技術專業一般在計算機學院,軟體工程專業一般在軟體學院。大三的專業課一般是計算機網絡作業系統等等。而且像離散數學這樣的專業課會安排很多課時,不僅學習時間長,難度也大。即便考試,也是試卷考試,很少有機考。整個大學四年下來,計算機科學與技術專業的學生動手能力比較差。軟體工程專業不一樣,由於成立較晚,教學體系並沒有計算機科學與技術專業那麼成熟。
  • 2020重大科學問題和工程技術難題發布,猜猜看涉及中醫藥的是什麼?
    央廣網北京8月15日消息(記者覃勇)為研判未來科技發展趨勢,抓住科技創新突破口,前瞻謀劃和布局前沿科技領域與方向,經過網絡初評投票、覆審評議和終審評議,15日,中國科協在第二十二屆中國科協年會閉幕式上發布了10個對科學發展具有導向作用的科學問題和10個對技術和產業具有關鍵作用的工程難題。
  • 中國科協發布20個重大科學問題和工程技術難題—新聞—科學網
    10個對技術和產業具有關鍵作用的工程難題。 10個工程技術難題為: 如何開發新型免疫細胞在腫瘤治療中的新途徑與新技術? 水平起降組合動力運載器一體化設計為何成為空天技術新焦點? 如何實現農業重大入侵生物的前瞻性風險預警和實時控制? 信息化條件下國家關鍵基礎設施如何防範重大電磁威脅? 矽光技術能否促成光電子和微電子的融合?
  • 《面向未來的科技——2020重大科學問題和工程技術難題解讀》圖書...
    8月15日,在第二十二屆中國科協年會閉幕式上,中國科學技術協會舉辦《面向未來的科技——2020重大科學問題和工程技術難題解讀》圖書發布會。  本書由中國科學技術協會主編,18家全國學會組織相關專家參與編寫,以解讀問題和難題的形成背景、國際發展趨勢,分析可能的解決方案,預測未來的發展前景為主要內容,分為「重大科學問題」與「工程技術難題」兩個部分。
  • 電子與計算機工程:一門將軟體設計與硬體設計一體化的專業!
    研究領域電子與計算機工程專業主要研究電子技術和計算機科學結合後綜合運用,本專業學生主要學習專業的電子工程、軟體設計和軟硬體集成綜合技能,還涉及數學、物理相關知識,主要研究微處理器、個人電腦、超級計算機、作業系統和電路設計,特別是集成電路設計等內容,為嵌入式系統、微處理系統、分布式系統、超大規模集成系統設計軟體代碼和固件;
  • 計算機科學與技術和軟體工程都能從事哪些工作?
    計算機作為一個專業大類包括:計算機科學與技術、軟體工程、網絡工程、信息安全等專業,其中計算機科學與技術是最基礎、核心的一個專業,更是各大院校計算機系開設的主要專業,專業學習過程中涵蓋了整個計算機生態體系比如硬體、軟體、網絡,全面並系統地學習了計算機有關知識體系,在本科學習階段理論偏多
  • 大學專業 | 計算機與軟體工程有什麼區別與聯繫
    計算機專業在20世紀50年代(1956年左右)就開始設立,而軟體工程專業是2002年國家教育部新增的專業,在這之前,軟體工程實際上是計算機科學與技術專業的一個方向。數字電路等與計算機硬體相關的內容;軟體工程專業會更側重軟體的設計,工程意識的培養,現代軟體動不動就上百萬行,如果缺少一個優秀的軟體工程師設計出一個好的架構,到後面軟體很可能就無法再修改維護下去了,只能作廢。
  • 大學專業|計算機與軟體工程有什麼區別與聯繫
    高考完填報志願時,如果對計算機相關專業感興趣,想要填報計算機專業,或許會發現,計算機專業旁邊常常還有一個軟體工程專業,那二者有什麼區別與聯繫呢?計算機專業在20世紀50年代(1956年左右)就開始設立,而軟體工程專業是2002年國家教育部新增的專業,在這之前,軟體工程實際上是計算機科學與技術專業的一個方向。兩個專業在一些課程上有所重合,都需要學習計算機科學的基礎課程,掌握計算機基礎知識包括程序設計語言、數據結構、計算機組成原理、計算機網絡、作業系統,這是兩者的聯繫。
  • 計算機類專業:電子信息、軟體工程、網絡工程對應的高校怎麼選?
    計算機類專業:電子信息、軟體工程、網絡工程對應的高校怎麼選?「計算機類」專業,是目前的熱門專業,他是由9個細分專業組成,因此在志願填報和考研、就業中,大家經常會用到一些常用名詞,計算機類專業因其就業前景好,薪酬高,而倍受家長、考生的青睞。
  • 軟體工程
    【可考證書】(1)大學期間英語四六級,計算機一二級,普通話證書都可以考。(2)軟體資格證書的考試,可以考軟體工程師,軟體測試師,都是中級的。考企業方面的證書,思科、惠普。  (3)軟體工程師要考國家教育部的《全國計算機等級考試證書》,有一級、二級、三級和四級。
  • 軟體工程與計算機科學技術的區別(第十一期)
    一、專業介紹培養學生系統掌握軟體工程基礎理論,以及軟體開發,軟體管理等應用技術,使學生成為具備軟體分析,設計,開發,維護能力,工程項目組織、管理能力以及團隊協作能力的高層次實用型、複合型人才。三、主要課程(大學)程序設計基礎,數據結構,算法分析與設計,離散數學,計算機組成原理,作業系統,編譯原理,計算機網絡,資料庫原理與設計,軟體工程基礎,軟體需求工程與建模,軟體體系結構與設計模式,軟體項目管理和軟體測試技術的等。