分類
發燒車訊

不敵客訴壓力 澳洲超市延後對塑膠袋收費

摘錄自2018年8月3日蘋果日報澳洲報導

澳洲一間大型連鎖超市原本要實施塑膠袋需收費政策,希望可推動「減塑」為環保出一分力,不料顧客對此大感不滿,政策推出後已經第2次延長收費期限。

連鎖超市科爾斯(Coles)自上月1日起停用一次性塑膠袋,改向顧客提供可重複使用、質料更耐用的塑膠袋作過渡,直至本周三(1日)為止。超市原定顧客此後若索取塑膠袋,每個須收取15澳分(約3.4元台幣)。

然而科爾斯周三發聲明指,自禁用一次性塑膠袋後,有顧客反映需要更多過渡時間,以適應使用可重複使用塑膠袋,集團因此決定在昆士蘭、新南威爾斯、維多利亞及西澳洲等地繼續提供免費塑膠袋至8月29日,而南澳洲、塔斯馬尼亞等地區則仍繼續收費。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

分類
發燒車訊

湄公河跨國水資源爭奪戰 寮國沙耶武里大壩爭議中即將啟用

環境資訊中心外電;姜唯 翻譯;林大利 審校;稿源:ENS

位於寮國北部湄公河的沙耶武里水壩將在數日內正式啟用。沙耶武里水壩是湄公河主流下游的第一座水壩,它的啟用象徵著湄公河命運的重要轉折點。

湄公河長4,350公里,是世界第12長河,排水量世界第八。發源於青藏高原,流經中國、緬甸、寮國和泰國,接著湧入柬埔寨和越南的沖積平原和三角洲。

沙耶武里水壩的主要目的是水力發電,其95%的發電量將由泰國電力局購買。

沙耶武里水壩打從一開始就是一個爭議性的工程,許多人擔心它對河流系統的作用,包括使湄公河的洄游魚類和沈積物難以往下游移動,可能連鄰國都會受影響。

大壩對環境的影響進而威脅流域內居民的糧食來源、生計和社會文化體系。

沙耶武里水壩即將完工。照片來源:

許多專家認為,湄公河上游中國境內已經建了六座水壩,寮國甚至柬埔寨下游還要再蓋,已經讓湄公河深陷危機。

沙耶武里水壩諮詢過程中,有許多利害關係者表示關切,質疑資料和研究是否充分。

越南政府呼籲暫停所有主流上水壩的建設10年,以進一步研究、更深入地了解河流系統和水壩的可能影響。

泰國湄公河沿岸的社區代表於2012年向泰國行政法院提起訴訟,質疑泰國向沙耶武里水壩購買電力的計畫。此訴訟案別具指標性,但經過數次上訴,七年後的今日仍懸而未決。

儘管如此,沙耶武里水壩的開發並沒有停止,開發商重新設計以減輕疑慮。

後續的大壩工程計畫也持續在進行。本月,湄公河委員會宣布開始對湄公河下游的第五座主流水壩瑯勃拉邦進行事前諮商。

在沙耶武里水壩啟用前,美國非營利組織國際河網(International Rivers)發布了關於水壩的新報告。該組織邀請兩位獨立專家針對湄公河委員會今年稍早發布的沙耶武里水壩設計變更審查報告發表評論。

兩位專家分別是澳洲雪梨大學人文地理學教授賀屈(Philip Hirsch)博士和英格蘭諾桑比亞大學社會科學副教授亨森格斯(Oliver Hensengerth)博士。他們檢視沙耶武里水壩如何成為主流水壩決策模式的基準,強調「迫切需要一個真正的區域性標準程序來保護湄公河的未來。」

國際河網的聲明指出,雖然國際河網自身的立場是認為大壩的開發正在「扼殺」湄公河,但該專家評論的目的並非批評或評估湄公河委員會的管理審查報告,而是在試圖「找出關鍵點,討論它們對沙耶武里水壩和其他規劃中或興建中水壩對湄公河下游主流以及該地區內的影響。」

沙耶武里水壩開發前的地景樣貌。照片來源: (CC BY 2.0)

23日,一場針對國際河網報告的座談會在泰國曼谷外國記者俱樂部舉行,與會學者、社區和民間社團熱烈討論沙耶武里水壩工程的歷史、決策過程缺陷、進行中的活動以及對生態系統與居民的影響。

國際河網認為,沙耶武里水壩興建過程「工程先行,研究後補」的做法很不負責任。(詳見)

湄公河沖積平原和三角洲是全世界農業產量極高、生物多樣性極豐富的水域之一,但是海平面上升、土地沉降、上游超過126個規劃中水壩以及各式各樣三角洲水利基礎設施讓人們不得不對水力發電的潛在問題感到憂心。

國際河網不是唯一一個對湄公河流域感到擔憂的環境組織。

丹麥DHI顧問集團針對湄公河三角洲進行的研究得出的結論是,「即使是現有最佳的魚道技術,也可能無法因應大量的魚類遷徙。在高峰時期,遷徙魚群最多可達每小時300萬條。此外,可能也難以滿足該流域數百種魚類有百百種遷移方式。」

根據22日在寮國首都永珍發布的最新報告《》,負責管理流域水壩開發的湄公河委員會也對此表示關注。

湄公河委員會成立於1995年,是一個政府間組織,直接與柬埔寨、寮國、泰國和越南政府合作,共同管理共享水資源和湄公河的永續發展。

該組織是水務外交的區域平台,也是維持該區域永續發展的水資源管理知識中心。

湄公河委員會報告警告:「主流水流體系明顯永久性改變,沉積物被阻攔造成泥沙流量大量減少,濕地持續喪失,河流生態環境惡化,捕撈漁業的壓力不斷增加以及目前水開發設施和用水資訊共享有限」,是湄公河流域國家面臨的主要挑戰。

「我們現在必須解決這些問題,盡可能減少對環境的損害,並在僅剩的濕地和河邊生態環境消失之前加以保護,同時利用更穩定且有所增加的旱季流量,實現湄公河地區最佳永續發展。」湄公河委員會執行長An Pich Hatda博士在啟用儀式上對來自四個湄公河國家、近100位官員說。

最新的流域狀況報告建議:「必須緊急採取更積極的區域性流域規劃和管理方法,並加強系統性地共享資訊,並嚴格監控河流流量,以因應這些流域挑戰。」

Dam Development Is ‘Silencing’ the Mekong River BANGKOK, Thailand, October 24, 2019 (ENS)

 In five days, the Xayaburi Hydropower Project on the Mekong River in northern Laos will formally begin operations. As the first dam on the lower Mekong mainstream, this marks a turning point for the Mekong River.

The Mekong River is 4,350 kilometers (2,703 miles) long, ranked 12th in length and eighth in water discharge in the world. The river originates in the Tibetan Plateau and flows through China, Myanmar, Laos, and Thailand before pouring into the alluvial floodplains and delta in Cambodia and Vietnam.

The main purpose of the Xayaburi dam is to produce hydroelectric power, 95 percent of which is to be purchased by the Electricity Generating Authority of Thailand.

From the outset, the Xayaburi dam was a controversial project due to widespread concerns over its expected impacts on the river system, including transboundary impacts in neighboring countries.

Major predicted impacts include the destruction of Mekong migratory fisheries and trapping of sediment, preventing it from traveling downstream.

The dam’s environmental impacts, in turn, threaten the food, livelihoods and socio-cultural systems of populations residing within the river basin.

Many experts believe that the Mekong, already suffering from the impacts of six dams installed in China on the Upper Mekong, and with more dams planned downstream in Laos and possibly Cambodia, is in crisis.

During the Xayaburi dam consultation process, many stakeholders raised concerns over the project and questioned the adequacy of the data and studies.

The Vietnamese government called for a project suspension and a 10-year moratorium on all mainstream dams pending further study to better understand the river system and the impacts of planned dams.

In Thailand, community representatives along the Mekong River filed a landmark lawsuit in the Thai Administrative Court challenging Thailand’s power purchase from the project. Originally filed in 2012, following several appeals, the lawsuit remains pending more than seven years later.

Despite this, the Xayaburi dam moved forward, with the developers undertaking a redesign in an effort to mitigate concerns.

Subsequent dam projects have followed. This month, the Mekong River Commission announced the commencement of Prior Consultation for Luang Prabang, the fifth lower Mekong mainstream dam to undergo the process.

In the lead-up to the commissioning of the Xayaburi dam, the U.S.-based nonprofit group International Rivers issued a new report on the dam. The group asked two independent experts to provide comments on the Mekong River Commission’s review of the Xayaburi redesign, released earlier this year.

The report of the experts, Dr. Philip Hirsch, professor of Human Geography at the University of Sydney, Australia; and Dr. Oliver Hensengerth, associate professor of social sciences at Northumbria University, England, examines the pattern of Xayaburi in setting a benchmark for decisions on mainstream dams and highlights “the urgent need for a truly regional approach to safeguard the Mekong’s future.”

Although International Rivers says dam development is “silencing” the Mekong River, this expert commentary is not intended as a critique or assessment of the MRC Review, said the group in a statement. “Rather, it seeks to draw out key points and discuss their implications for Xayaburi and other dams under construction or consideration on the lower Mekong mainstream and within the region.”

On Wednesday, a panel discussion of the International Rivers report with academic, community and civil society speakers at the Foreign Correspondents’ Club of Thailand in Bangkok provoked comments on the project’s history, its flawed decision-making process, the ongoing campaigns, and Xayaburi’s implications for the ecosystems and people of the Mekong Basin.

International Rivers has described the “build first, study later” approach propagated by the Xayaburi Dam process as “a dangerously irresponsible model for dam-building in the Mekong.”

To read the report, “Review of Design Changes Made for the Xayaburi Hydropower Project,” click .

The Mekong floodplains and delta are among the most agriculturally productive and biologically diverse waterscapes of the world, but sea level rise, land subsidence, and the proposed upstream development of over 126 hydropower dams and extensive delta-based water infrastructure have raised concerns about the potential impacts on the hydrology of the region.

International Rivers is not the only environmental group with concerns about the Mekong River Basin.

A Mekong Delta Study conducted by Denmark’s DHI Consulting Group concluded it was likely “that even the best available fish passage technologies’ may not be able to handle either the massive volume of fish migrations, which during peak periods can reach up to three million fish per hour, or the diversity of migration strategies that characterise the hundreds of fish species in the basin.”

The Mekong River Commission, which governs the dam development of the basin, is also concerned, according to the latest report, State of the Basin Report 2018, released on Tuesday in Vientiane, the Laotian capital city.

Established in 1995, the Mekong River Commission, MRC, is an inter-governmental organization that works directly with the governments of Cambodia, Laos, Thailand, and Vietnam to jointly manage the shared water resources and the sustainable development of the Mekong River.

The organization serves as a regional platform for water diplomacy as well as a knowledge hub of water resources management for the sustainable development of the region.

The MRC report warns, “The apparent permanent modification of mainstream flow regime, the substantial reduction in sediment flows due to sediment trapping, the continuing loss of wetlands, the deterioration of riverine habitats, the growing pressures on capture fisheries, and the limited information sharing on current water development facilities and water use,” are some of the major challenges facing countries in the Mekong Basin.

“We need to address these issues now in order to minimize further environmental harm and protect remaining wetlands and riverine habitats before they are gone, while leveraging the benefits of more secure and increased dry season flows and achieving a more optimal and sustainable development of the Mekong basin,” Dr. An Pich Hatda, chief executive officer of the MRC Secretariat, told nearly 100 officials from the four MRC countries at the launch ceremony.

This latest State of the Basin Report advises that “a more proactive regional approach to basin planning and management, with an enhanced and systematic information sharing mechanism and robust monitoring of river flow must be put in place urgently to address these basin-wide challenges.”

※ 全文及圖片詳見:

作者

如果有一件事是重要的,如果能為孩子實現一個願望,那就是人類與大自然和諧共存。

於特有生物研究保育中心服務,小鳥和棲地是主要的研究對象。是龜毛的讀者,認為龜毛是探索世界的美德。

延伸閱讀

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

分類
發燒車訊

遊客遽增破壞環境 紐西蘭國會報告示警

摘錄自2019年12月18日中央社威靈頓報導

紐西蘭國會今天(18日)提出報告警示,遽增的遊客數量正危害紐西蘭環境,也讓當地聞名遐邇、極具魅力的寧靜印象受到逐步破壞。

法新社報導,紐西蘭一向標榜「100%純淨」和「乾淨又環保」的形象,但近年來遊客數量遽增,許多人朝聖「魔戒」(The Lord of the Rings)電影拍攝地點大玩自拍,還有登山客、健行者及野生動物愛好人士。

紐西蘭國會環境事務專員厄普頓(Simon Upton)提出報告之際,紐國旅遊業正受到嚴格審查,因為白島(White Island)火山在9日爆發,造成16名外籍旅客和2名導遊喪命。

擁有490萬人口的紐西蘭每年吸引近400萬名外國遊客,厄普頓說,這項數據到了2050年可能增加2倍,厄普頓表示,基礎設施變得吃緊、環境承受壓力,紐西蘭原有的諸多品質正在消失,他說:「大批群眾正逐漸破壞許多外國遊客赴紐西蘭旅遊所尋覓的獨處感、寧靜和親近大自然的感覺,我們必須要問:我們是否正在殺雞取卵?」

厄普頓提到,紐西蘭人也是問題的一部分,在紐西蘭國定假期,著名景點湧現的國內遊客人潮比外國遊客還要多。

他還說,紐西蘭人已經習慣東加里羅步道(Tongariro Crossing)等著名景點「被遊客團團包圍」,而且問題只會持續惡化。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

分類
發燒車訊

南非減塑大功臣 寶特瓶廢棄塑膠製成磚

摘錄自2020年2月14日公視報導

在全球都出現塑膠垃圾問題時,南非開始用寶特瓶跟廢棄塑膠包裝,做成環保磚頭,來蓋托兒所等建築,成功減少塑膠垃圾。

根據2018年的「南非廢棄物狀況報告」指出,南非在2017年製造的4200萬噸廢物中,只有約11%被回收再利用。而2012年成立的南非當地民間團體「Waste-ED」,主要協助國家解決廢棄物品問題。除了教育學童相關觀念,還接受諮詢,引進這種塑膠瓶環保磚的製作,用來蓋學校或是簡易建築。

這種塑膠瓶環保磚,起源於菲律賓北部,後來應用在無法解決塑膠垃圾問題的發展中國家,協助當地政府廢物利用。目前開普敦郊區,已經有許多建築,包括托兒中心等建築牆壁,都是用這些環保磚製作。目前開普敦有超過2萬個、塑膠瓶環保磚的收集點,還跟學校合作,帶學童們一起參與製作跟使用環保磚。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

分類
發燒車訊

對照圖鑑也會誤判 日本野菇中毒事件頻傳

文:宋瑞文

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

FB行銷專家,教你從零開始的技巧

分類
發燒車訊

程序員的命名素養

引言

今天來聊聊命名相關內容。

在日常工作中,項目、類、方法、表等等等等,都需要我們起名來標識區分。好的名字讓人賞心悅目,不好的名字讓人看的想吐。

最近工作有幸寫了node、前端、php、sql、scala,也見識了公司各位前輩們的命名功底。其中不乏abc命名、拼音命名、蹩腳英文命名,更有不少從別的地方粘過來連名都不改的操作。

命名沒有對錯,只是規範一點,可以提高可讀性、可維護性。

命名原則

拼寫正確

拼寫正確是可讀的基礎。

play shiftplay shit自己體會一下

清新明了,見名知意

根據要表達的內容命名,一針見血。

getNameById 根據id獲取名稱
ClassLoader 類加載器
MYSQL_USERNAME mysql用戶名 

如上幾個例子,我們一眼就知道要表達什麼,可讀性高。

使用英文字母命名

在編程中,英文還是較為主流的,最好使用單詞來命名,再不濟也是用拼音來命名。

不論是拼音或單詞,清晰表意是首要。

保持一致

在一個項目中,應該使用統一的規範來命名。

無規矩不成方圓。

合理使用動詞名詞

類名、變量名通常應使用名詞。如ClassLoaderuserId

對於方法名、函數名,應包含動詞。如handleClickgenerateUniqueId

命名方法

常見的命名方法有駝峰命名法、匈牙利命名法、帕斯卡命名法、中/下劃線命名法

駝峰命名法Camel-Case

駝峰命名法,又叫小駝峰命名法,如名稱所表達的意思,指混合使用大小寫字母老表示名字。

userIdgetCompanyNameById

應用很廣泛。

匈牙利命名法

基本原則是:變量名=屬性+類型+對象描述。通過名稱可以直觀的了解他的所屬、類型等信息。

是早期的命名方式,早期IDE沒有很智能的時候,這種命名是很有必要的。

iNum,表示int類型的num

現在依舊很少有人用了。

帕斯卡命名法

又叫大駝峰命名法,就是把駝峰命名的首字母大寫了。

ClassLoader

中/下劃線命名法

單詞全部小寫,單詞和單詞間用中劃線或下劃線分割。

user_idpython-flask-demo

下劃線命名在數據庫中較為常見。

常量命名法

這個不是官方的方法,但是常量一般是由固定規範的。

格式:所有單詞的所有字母都是大寫,單詞之間用下戶線連接。

APOLLO_NAMESPAC

總結

好的命名習慣是每個程序員必備的基本素養。

寫代碼時,好的命名會讓思路更加清洗,代碼寫的更加絲滑。

代碼就是程序員的形象,從命名的細節開始,讓自己更帥一些。

個人公眾號:碼農峰,定時推送行業資訊,持續發布原創技術文章,歡迎大家關注。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

分類
發燒車訊

重識Java8函數式編程

前言

最近真的是太忙忙忙忙忙了,很久沒有更新文章了。最近工作中看到了幾段關於函數式編程的代碼,但是有點費解,於是就準備總結一下函數式編程。很多東西很簡單,但是如果不總結,可能會被它的各種變體所困擾。接觸Lambda表達式已經很久了,但是也一直是處於照葫蘆畫瓢的階段,所以想自己去編寫相關代碼,也有些捉襟見肘。

1. Lambda表達式的不同形式

// 基本形式
參數 -> 主體

1.1 形式一

Runnable noArguments = () -> System.out.println("Hello World");

該形式的Lambda表達式不包含參數,使用空括號()表示沒有參數。它實現了Runnable接口,該接口也只有一個run方法,沒有桉樹,且返回類型為void。

1.2 形式二

ActionListener oneArgument = event -> System.out.println("button clicked");

該形式的Lambda表達式包含且只包含一個參數,可省略參數的符號。

1.3 形式三

Runnable multiStatement = () -> {
	System.out.print("Hello"); 
    System.out.println(" World"); 
};

Lambda表達式的主體不僅可以使一個表達式,而且也可以是一段代碼塊,使用大括號{}將代碼塊括起來。該代碼塊和普通方法遵循的規則別無二致,可以用返回或拋出異常來退出。只有以行代碼的Lambda表達式也可以使用大括號,用以明確Lambda表達式從何處開始,到哪裡結束。

1.4 形式四

BinaryOperator<Long> add = (x, y) -> x + y;

Lambda表達式也可以表示包含多個參數的方法,上面的Lambda表達式並不是將兩個数字相加,而是創建了一個函數,用來計算兩個数字相加的結果。變量add的類型時BinaryOperator ,它不是兩個数字的和,而是將兩個数字相加的那行代碼。

1.5 形式五

BinaryOperator<Long> addExplicit = (Long x, Long y) -> x + y;

到目前為止,所有Lambda表達式中的參數類型都是由編譯器推斷得出的。但有時最好也可以显示聲明參數類型,此時就需要使用小括號將參數括起來,多個參數的情況也是如此。

2. 引用值,而不是變量

如果你曾使用過匿名內部類,也許遇到過這樣的情況:需要引用它所在方法里的變量。這是,需要將變量聲明為final。

final String name = getUserName(); 
button.addActionListener(new ActionListener() {
	public void actionPerformed(ActionEvent event) { 
        System.out.println("hi " + name); 
    } 
});

將變量聲明為 final,意味着不能為其重複賦 值。同時也意味着在使用 final 變量時,實際上是在使用賦給該變量的一個特定的值。

Java 8 雖然放鬆了這一限制,可以引用非 final 變量,但是該變量在既成事實上必須是 final(意思就是你不能再次對該變量賦值)。雖然無需將變量聲明為 final,但在 Lambda 表達式中,也無法用作非終態變量。如 果堅持用作非終態變量,編譯器就會報錯。 既成事實上的 final 是指只能給該變量賦值一次。換句話說,Lambda 表達式引用的是值, 而不是變量。

例如:

String name = getUserName(); 
button.addActionListener(event -> System.out.println("hi " + name));

3. 函數接口

在 Java 里,所有方法參數都有固定的類型。假設將数字 3 作為參數傳給一個方法,則參數 的類型是 int。那麼,Lambda 表達式的類型又是什麼呢?

使用只有一個方法的接口來表示某特定方法並反覆使用,是很早就有的習慣。使用 Swing 編寫過用戶界面的人對這種方式都不陌生,這裏無需再標新立異,Lambda 表達式也使用同樣的技巧,並將這種接口稱為函數接口。

接口中單一方法的命名並不重要,只要方法簽名和 Lambda 表達式的類型匹配即可。可在函數接口中為參數起一個有意義的名字,增加代碼易讀性,便於更透徹 地理解參數的用途。

3.1 Java中重要的函數接口

接口 參數 返回類型 示例
Predicate T boolean 判斷是否
Consumer T void 輸出一個值
Function<T,R> T T 獲得對象的名字
Supplier None T 工廠方法
UnaryOperator T T 邏輯非(!)
BinaryOperator (T, T) T 求兩個數的乘積(*)

3.2 函數接口定義

定義函數接口需要使用到註解@FunctionalInterface

例如:

@FunctionalInterface
public interface MyFuncInterface {
	void print();
}

使用:

public class MyFunctionalInterfaceTest {
    public static void main(String[] args) {
        doPrint(() -> System.out.println("java"));
    }

    public static void doPrint(MyFuncInterface my) {
        System.out.println("請問你喜歡什麼編程語言?");
        my.print();
    }
}

說明:

這隻是一個很簡單的例子,有人覺得為什麼要搞這麼複雜,去定義一個接口?這個問題還是讀者在平時的工作中去感悟吧,總之,先學會怎麼用它。不至於看了別人寫的代碼都看不懂。

至於我個人的理解,可以簡單聊聊。以前寫過JavaScript,裏面有一種語法就是將自定義函數B作為參數傳遞到另外一個函數A裏面,在函數A裏面會執行你自定義的函數B邏輯,我當時就非常喜歡這種特性,因為每個人關於函數B的實現可能不一樣,亦或者場景不一樣也會導致函數B的實現不一樣。我覺得Java8的這個函數式編程就是對這一特性的補充。

4. 流

流的常用操作有很多,例如collect(toList())mapfiltermaxmin等,下面介紹一下flatMapreduce

4.1 flatMap

flatMap 方法可用 Stream 替換值,然後將多個 Stream 連接成一個 Stream。

List<Integer> together = Stream.of(asList(1, 2), asList(3, 4)) 				 
    .flatMap(numbers -> numbers.stream())
    .collect(toList()); 
assertEquals(asList(1, 2, 3, 4), together);

調用 stream 方法,將每個列錶轉換成 Stream 對象,其餘部分由 flatMap 方法處理。 flatMap 方法的相關函數接口和 map 方法的一樣,都是 Function 接口,只是方法的返回值 限定為 Stream 類型罷了。

4.2 reduce

reduce 操作可以實現從一組值中生成一個值。對於 count、min 和 max 方 法,因為常用而被納入標準庫中。事實上,這些方法都是 reduce 操作。

如何通過 reduce 操作對 Stream 中的数字求和。以 0 作起點——一個空Stream 的求和結果,每一步都將 Stream 中的元素累加至 accumulator,遍歷至 Stream 中的 最後一個元素時,accumulator 的值就是所有元素的和。

int count = Stream.of(1, 2, 3)
    .reduce(0, (acc, element) -> acc + element); 
assertEquals(6, count);

Lambda 表達式的返回值是最新的 acc,是上一輪 acc 的值和當前元素相加的結果。reducer 的類型是前面已介紹過的 BinaryOperator。

5. Optional

reduce 方法的一個重點尚未提及:reduce 方法有兩種形式,一種如前面出現的需要有一 個初始值,另一種變式則不需要有初始值。沒有初始值的情況下,reduce 的第一步使用 Stream 中的前兩個元素。有時,reduce 操作不存在有意義的初始值,這樣做就是有意義的,此時,reduce 方法返回一個 Optional 對象。

Optional 是為核心類庫新設計的一個數據類型,用來替換 null 值。人們對原有的 null 值有很多抱怨。人們常常使用 null 值表示值不存在,Optional 對象能更好地表達這個概念。使用 null 代 表值不存在的最大問題在於 NullPointerException。一旦引用一個存儲 null 值的變量,程 序會立即崩潰。使用 Optional 對象有兩個目的:首先,Optional 對象鼓勵程序員適時檢查變量是否為空,以避免代碼缺陷;其次,它將一個類的 API 中可能為空的值文檔化,這比閱讀實現代碼要簡單很多。

下面我們舉例說明 Optional 對象的 API,從而切身體會一下它的使用方法。使用工廠方法 of,可以從某個值創建出一個 Optional 對象。Optional 對象相當於值的容器,而該值可以 通過 get 方法提取。

Optional<String> a = Optional.of("a"); 
assertEquals("a", a.get());

Optional 對象也可能為空,因此還有一個對應的工廠方法 empty,另外一個工廠方法 ofNullable 則可將一個空值轉換成 Optional 對象。下面的代碼同時展示 了第三個方法 isPresent 的用法(該方法表示一個 Optional 對象里是否有值)。

Optional emptyOptional = Optional.empty(); 
Optional alsoEmpty = Optional.ofNullable(null); assertFalse(emptyOptional.isPresent());

使用 Optional 對象的方式之一是在調用 get() 方法前,先使用 isPresent 檢查 Optional 對象是否有值。使用 orElse 方法則更簡潔,當 Optional 對象為空時,該方法提供了一個 備選值。如果計算備選值在計算上太過繁瑣,即可使用 orElseGet 方法。該方法接受一個 Supplier 對象,只有在 Optional 對象真正為空時才會調用。

assertEquals("b", emptyOptional.orElse("b")); 
assertEquals("c", emptyOptional.orElseGet(() -> "c"));

最後

實踐是檢驗真理的唯一標準,多寫代碼,多思考,你的代碼才會越來越好。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

分類
發燒車訊

記一次uboot升級過程的兩個坑

背景

之前做過一次uboot的升級,當時留下了一些記錄,本文摘錄其中比較有意思的兩個問題。

啟動失敗問題

問題簡述

uboot代碼中用到了一個庫,考慮到庫本身跟uboot版本沒什麼關係,就直接把舊的庫文件拷貝過來使用。結果編譯鏈接是沒問題,啟動卻會卡住。

消失的打印

為了明確卡住的位置,就去修改了庫的源碼,添加一些打印(此時還是在舊版本uboot下編譯的),結果發現卡住的位置或隨着添加打印的變化而變化,且有些打印語句,添加后未打印出來。

我決定先從這些神秘消失的打印入手。

分析下uboot中的printf實現,最底層就是寫寄存器,是一個同步的函數,也沒什麼可疑的地方。

為了確認打印不出來的時候,到底有沒有調用到printf,我決定給printf增加一個計數器,在gd結構體中,增加一個printf_count字段,初始化為0,每次打印時執行printf_count++並打印出值。

設計這個試驗,本意是確認未打印出來時是否確實也調用到了printf,但卻有了別的發現,實驗結果中printf_count值會異常變化,不是按打印順序遞增,而是會突變成很大的異常值。

printf_countgd結構體的成員,那就是gd的問題了。進一步將uboot全局結構體gd的地址打印出來。確認了原因是gd結構體的指針變化了。

這也可以解釋部分打印消失的現象,原因是我們在gd中有另一個字段,用於控制打印等級。當gd被改動了,printf就可能解析出錯,誤以為打印等級為0而提前返回。

gd的實現

那麼好端端的,gd為什麼會被改了呢?這就要先看看gd到底是怎麼實現的了。

uboot中維護了一個全局的結構體gd。在代碼中加入

DECLARE_GLOBAL_DATA_PTR;

即可使用gd指針訪問這個全局結構體,許多地方都會藉助gd來保存傳遞信息。

進一步看看這個宏的定義

舊版本uboot:
#define DECLARE_GLOBAL_DATA_PTR        register volatile gd_t *gd asm ("r8")

新版本uboot:
#define DECLARE_GLOBAL_DATA_PTR        register volatile gd_t *gd asm ("r9")

居然不一樣,一個是將gd的值放到r8寄存器,一個是放在r9寄存器。

那麼就可以猜測到,庫是在舊版本uboot中編譯出來的,可能使用了r9,那麼放到新版本uboot中去,就會破壞r9寄存器中保存的gd值,導致一系列依賴gd的代碼不能正常工作。

驗證改動

為了求證,將庫反彙編出來,發現確實避開了r8寄存器,但使用了r9寄存器。

說明uboot在指定gd寄存器的同時,還有某種方法讓其他代碼不使用這個寄存器。

那是不是把舊uboot中的這個r8改成r9,重新編譯庫就可以了呢?試一下,還是不行。

那麼禁止其他代碼使用r8寄存器肯定就是通過別的方式實現的了。簡單粗暴地在舊版本uboot下搜索r8,去掉.c .h等類型后,很容易發現了

./arch/arm/cpu/armv7/config.mk:24:PLATFORM_RELFLAGS += -fno-common -ffixed-r8 -msoft-floa

-ffixed-r8修改為-ffixed-r9,重新編譯出庫,這回就可以正常工作了,打印正常,啟動正常。反彙編出來也可以看到,新編譯出來的庫用了r8沒有用r9

當然更好的改法,是直接在新版本的uboot中編譯,這是最可靠的。

追本溯源

話說回來,為什麼兩個版本的uboot,會使用不同的寄存器呢?難道有什麼坑?

這就得去翻一下git記錄了。

commit fe1378a961e508b31b1f29a2bb08ba1dac063155
Author: Jeroen Hofstee <jeroen@myspectrum.nl>
Date:   Sat Sep 21 14:04:41 2013 +0200

    ARM: use r9 for gd
    
    To be more EABI compliant and as a preparation for building
    with clang, use the platform-specific r9 register for gd
    instead of r8.
    
    note: The FIQ is not updated since it is not used in u-boot,
    and under discussion for the time being.
    
    The following checkpatch warning is ignored:
    WARNING: Use of volatile is usually wrong: see
    Documentation/volatile-considered-harmful.txt
    
    Signed-off-by: Jeroen Hofstee <jeroen@myspectrum.nl>
    cc: Albert ARIBAUD <albert.u.boot@aribaud.net>

git記錄中,也可以確認完整地將r8切換到r9,都需要做哪些修改

diff --git a/arch/arm/config.mk b/arch/arm/config.mk
index 16c2e3d1e0..d0cf43ff41 100644
--- a/arch/arm/config.mk
+++ b/arch/arm/config.mk
@@ -17,7 +17,7 @@ endif
 
 LDFLAGS_FINAL += --gc-sections
 PLATFORM_RELFLAGS += -ffunction-sections -fdata-sections \
-                     -fno-common -ffixed-r8 -msoft-float
+                     -fno-common -ffixed-r9 -msoft-float
 
 # Support generic board on ARM
 __HAVE_ARCH_GENERIC_BOARD := y
diff --git a/arch/arm/cpu/armv7/lowlevel_init.S b/arch/arm/cpu/armv7/lowlevel_init.S
index 82b2b86520..69e3053a42 100644
--- a/arch/arm/cpu/armv7/lowlevel_init.S
+++ b/arch/arm/cpu/armv7/lowlevel_init.S
@@ -22,11 +22,11 @@ ENTRY(lowlevel_init)
        ldr     sp, =CONFIG_SYS_INIT_SP_ADDR
        bic     sp, sp, #7 /* 8-byte alignment for ABI compliance */
 #ifdef CONFIG_SPL_BUILD
-       ldr     r8, =gdata
+       ldr     r9, =gdata
 #else
        sub     sp, #GD_SIZE
        bic     sp, sp, #7
-       mov     r8, sp
+       mov     r9, sp
 #endif
        /*
         * Save the old lr(passed in ip) and the current lr to stack
diff --git a/arch/arm/include/asm/global_data.h b/arch/arm/include/asm/global_data.h
index 79a9597419..e126436093 100644
--- a/arch/arm/include/asm/global_data.h
+++ b/arch/arm/include/asm/global_data.h
@@ -47,6 +47,6 @@ struct arch_global_data {
 
 #include <asm-generic/global_data.h>
 
-#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")
+#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r9")
 
 #endif /* __ASM_GBL_DATA_H */
diff --git a/arch/arm/lib/crt0.S b/arch/arm/lib/crt0.S
index 960d12e732..ac54b9359a 100644
--- a/arch/arm/lib/crt0.S
+++ b/arch/arm/lib/crt0.S
@@ -69,7 +69,7 @@ ENTRY(_main)
        bic     sp, sp, #7      /* 8-byte alignment for ABI compliance */
        sub     sp, #GD_SIZE    /* allocate one GD above SP */
        bic     sp, sp, #7      /* 8-byte alignment for ABI compliance */
-       mov     r8, sp          /* GD is above SP */
+       mov     r9, sp          /* GD is above SP */
        mov     r0, #0
        bl      board_init_f
 
@@ -81,15 +81,15 @@ ENTRY(_main)
  * 'here' but relocated.
  */
 
-       ldr     sp, [r8, #GD_START_ADDR_SP]     /* sp = gd->start_addr_sp */
+       ldr     sp, [r9, #GD_START_ADDR_SP]     /* sp = gd->start_addr_sp */
        bic     sp, sp, #7      /* 8-byte alignment for ABI compliance */
-       ldr     r8, [r8, #GD_BD]                /* r8 = gd->bd */
-       sub     r8, r8, #GD_SIZE                /* new GD is below bd */
+       ldr     r9, [r9, #GD_BD]                /* r9 = gd->bd */
+       sub     r9, r9, #GD_SIZE                /* new GD is below bd */
 
        adr     lr, here
-       ldr     r0, [r8, #GD_RELOC_OFF]         /* r0 = gd->reloc_off */
+       ldr     r0, [r9, #GD_RELOC_OFF]         /* r0 = gd->reloc_off */
        add     lr, lr, r0
-       ldr     r0, [r8, #GD_RELOCADDR]         /* r0 = gd->relocaddr */
+       ldr     r0, [r9, #GD_RELOCADDR]         /* r0 = gd->relocaddr */
        b       relocate_code
 here:
 
@@ -111,8 +111,8 @@ clbss_l:cmp r0, r1                  /* while not at end of BSS */
        bl red_led_on
 
        /* call board_init_r(gd_t *id, ulong dest_addr) */
-       mov     r0, r8                  /* gd_t */
-       ldr     r1, [r8, #GD_RELOCADDR] /* dest_addr */
+       mov     r0, r9                  /* gd_t */
+       ldr     r1, [r9, #GD_RELOCADDR] /* dest_addr */
        /* call board_init_r */
        ldr     pc, =board_init_r       /* this is auto-relocated! */

啟動慢問題

問題簡述

填了幾個坑之後,新的uboot可以啟動到內核了,但發現啟動速度非常慢,內核啟動速度慢了接近10倍!明明是同一個內核,為什麼差異這麼大。

排查寄存器

初步排查了下設備樹配置,以及uboot跳轉內核前的一些關鍵寄存器,確實在兩個版本的uboot中有所不同,但具體去看這些不同,發現都不會影響速度,將一些驅動對齊之後寄存器差異基本就消失了。

差異的分界

那再細看,kernel的速度有差異,uboot呢?在哪個時間點之後,速度開始產生差異?

嘗試在兩個版本的uboot中插入一些操作,對比時間戳,發現兩個uboot在某個節點之後的速度確實有區別。

進一步排查,原來是在打開cache操作之後,舊uboot的速度就會比新uboot快。嘗試將舊ubootcache關掉,則二者基本一致。嘗試將舊uboot操作cache的代碼,移植到新uboot,未發生改變。

此時可確認新uboot的開cache有問題。但覺得這個跟kernel啟動慢沒關係。因為uboot進入kernel之前都會關cache,由kernel自己去重新打開。

也就是不管是用哪份uboot,也不管uboot中是否開了cache,對kernel階段都應該沒有影響才對。

於是記錄下來uboot的這個問題,待後續修復。先繼續找kernel啟動慢的原因。(注:現在看來當時的做法是有問題的,這裏的異常這麼明顯,應該設法追蹤下去找出原因才對)

鎖定uboot

uboot的嫌疑非常大,但還不能完全確認,因為uboot之前還有一級spl。是否會是spl的問題呢?

嘗試改用新spl+舊uboot,啟動速度正常。而新spl+新uboot的啟動速度則很慢,其他因素都不變,說明問題確實出在uboot階段。

多做or少做

當時到這一步就卡住了,直接比較兩份uboot的代碼不太現實,差異太大了。

後來我就給自己提了個問題,到底新uboot是多做了某件事情,還是少做了某件事情?

換個說法,目前已知

spl --> 舊uboot --> kernel(速度快)
spl --> 新uboot --> kernel(速度快)

但到底是以下的情況A還是情況B呢?

A: spl(速度慢) --> 舊uboot(做了某個會提升速度的操作) --> kernel(速度快)
   spl(速度慢) --> 新uboot(少做了某個會提升速度的操作) --> kernel(速度慢)

B: spl(速度快) --> 舊uboot(沒做特殊操作) --> kernel(速度快)
   spl(速度快) --> 新uboot(多做了某個會限制速度的操作) --> kernel(速度慢)

為了驗證,我決定讓spl直接啟動內核,看看內核到底是快是慢。

支持過程碰到了一些小問題

1.spl沒有能力加載這麼大的kernel

解決:此時不需要kernel能完全啟動,只需要能加載啟動一段,足以體現出啟動速度是否正常即可,於是裁剪出一個非常小kernel來輔助實驗。

2.kernel需要dtb

解決:內核有一個CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE選項。選上重新編譯。編譯后再用ddkerneldtb拼接到一起,作為新的kernel。這樣,spl就只需要加載一個文件並跳轉過去即可。

試驗結果,spl啟動的kernel和使用新uboot啟動的kernel速度一致,均比舊uboot啟動的kernel慢。

說明,舊uboot中做了某個關鍵操作,而新uboot沒做。

找出關鍵操作

那接下來的任務就是,找出舊uboot中的這個關鍵操作了。

怎麼找呢?有了上一步的成果,我們可以使用以下方法來排查

  1. spl加載kernel和舊uboot

  2. spl跳轉到舊uboot,此時kernel其實已經在dram中準備好了,隨時可以啟動

  3. 在舊uboot的啟動流程各個階段,嘗試直接跳轉到kernel,觀察啟動速度

  4. 如果在舊ubootA點跳轉kernel啟動慢,B點跳轉啟動快,則說明關鍵操作位於AB點之間。

方法有了,很快就鎖定到start.S,進一步在start.S中揪出了這段代碼

#if defined(CONFIG_ARM_A7)
@set SMP bit
    mrc     p15, 0, r0, c1, c0, 1
    orr        r0, r0, #(1<<6)
    mcr        p15, 0, r0, c1, c0, 1
#endif

ubootstart.S中沒有這段代碼,嘗試在新ubootstart.S中添加此操作,速度立馬恢復正常了。

再全局搜索下,原來這個新版本uboot中,套路是在board_init中進行此項設置的,而這個平台從舊版本移植過來,就沒有設置 SMP bit, 補上即可。

SMP bit是什麼

SMP 是指對稱多處理器,看起來這個 bit 會影響多核的 cache一致性,此處沒有再深入研究。

但可以知道,對於單處理器的情況,也需要設置這個bit才能正常使用cache

貼下arm的圖和描述:

[6]	SMP	

Signals if the Cortex-A9 processor is taking part in coherency or not.

In uniprocessor configurations, if this bit is set, then Inner Cacheable Shared is treated as Cacheable. The reset value is zero.

搜下kernel的代碼,發現也是有地方調用了的。不過這個芯片是單核的,根本就沒配置CONFIG_SMP

#ifdef CONFIG_SMP
	ALT_SMP(mrc	p15, 0, r0, c1, c0, 1)
	ALT_UP(mov	r0, #(1 << 6))		@ fake it for UP
	tst	r0, #(1 << 6)			@ SMP/nAMP mode enabled?
	orreq	r0, r0, #(1 << 6)		@ Enable SMP/nAMP mode
	orreq	r0, r0, r10			@ Enable CPU-specific SMP bits
	mcreq	p15, 0, r0, c1, c0, 1
#endif

總結

整理出來一方面是記錄這兩個bug,另一方面也是想記錄下當時的一些操作。

畢竟同樣的bug可能以後都不會碰到了,但解bug的方法和思路卻是可以積累復用的。

blog: https://www.cnblogs.com/zqb-all/p/13172546.html
公眾號:https://sourl.cn/shT3kz

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

分類
發燒車訊

手把手教你學Numpy,搞定數據處理——收官篇

本文始發於個人公眾號:TechFlow,原創不易,求個關注

今天是Numpy專題第6篇文章,我們一起來看看Numpy庫當中剩餘的部分。

數組的持久化

在我們做機器學習模型的研究或者是學習的時候,在完成了訓練之後,有時候會希望能夠將相應的參數保存下來。否則的話,如果是在Notebook當中,當Notebook關閉的時候,這些值就丟失了。一般的解決方案是將我們需要的值或者是數組“持久化”,通常的做法是存儲在磁盤上。

Python當中讀寫文件稍稍有些麻煩,我們還需要創建文件句柄,然後一行行寫入,寫入完成之後需要關閉句柄。即使是用with語句,也依然不夠簡便。針對這個問題,numpy當中自帶了寫入文件的api,我們直接調用即可。

通過numpy當中save的文件是二進制格式的,所以我們是無法讀取其中內容的,即使強行打開也會是亂碼。

以二進制的形式存儲數據避免了數據類型轉化的過程,尤其是numpy底層的數據是以C++實現的,如果使用Python的文件接口的話,勢必要先轉化成Python的格式,這會帶來大量開銷。既然可以存儲,自然也可以讀取,我們可以調用numpy的load函數將numpy文件讀取進來。

要注意我們保存的時候沒有添加文件後綴,numpy會自動為我們添加後綴,但是讀取的時候必須要指定文件的全名,否則會numpy無法找到,會引發報錯。

不僅如此,numpy還支持我們同時保存多個數組進入一個文件當中。

我們使用savez來完成,在這個api當中我們傳入了a=arr,b=arr,其實是以類似字典的形式傳入的。在文件當中,numpy會將變量名和數組的值映射起來。這樣我們在讀入的時候,就可以通過變量名訪問到對應的值了。

如果要存儲的數據非常大的話,我們還可以對數據進行壓縮,我們只需要更換savez成savez_compressed即可。

線性代數

Numpy除了科學計算之外,另外一大強大的功能就是支持矩陣運算,這也是它廣為流行並且在機器學習當中大受歡迎的原因之一。我們在之前的線性代數的文章當中曾經提到過Numpy這方面的一些應用,我們今天再在這篇文章當中匯總一些常用的線性代數的接口。

點乘

說起來矩陣點乘應該是最常用的線代api了,比如在神經網絡當中,如果拋開激活函數的話,一層神經元對於當前數據的影響,其實等價於特徵矩陣點乘了一個係數矩陣。再比如在邏輯回歸當中,我們計算樣本的加權和的時候,也是通過矩陣點乘來實現的。

在Andrew的深度學習課上,他曾經做過這樣的實現,對於兩個巨大的矩陣進行矩陣相乘的運算。一次是通過Python的循環來實現,一次是通過Numpy的dot函數實現,兩者的時間開銷相差了足足上百倍。這當中的效率差距和Python語言的特性以及併發能力有關,所以在機器學習領域當中,我們總是將樣本向量化或者矩陣化,通過點乘來計算加權求和,或者是係數相乘。

在Numpy當中我們採用dot函數來計算兩個矩陣的點積,既可以寫成a.dot(b),也可以寫成np.dot(a, b)。一般來說我更加喜歡前者,因為寫起來更加方便清晰。如果你喜歡後者也問題不大,這個只是個人喜好。

注意不要寫成*,這個符號代表兩個矩陣元素兩兩相乘,而不是進行點積運算。它等價於np當中的multiply函數。

轉置與逆矩陣

轉置我們曾經在之前的文章當中提到過,可以通過.T或者是np.transpose來完成。

Numpy中還提供了求解逆矩陣的操作,這個函數在numpy的linalg路徑下,這個路徑下實現了許多常用的線性代數函數。根據線性代數當中的知識,只有滿秩的方陣才有逆矩陣。我們可以通過numpy.linalg.det先來計算行列式來判斷,否則如果直接調用的話,對於沒有逆矩陣的矩陣會報錯。

在這個例子當中,由於矩陣b的行列式為0,說明它並不是滿秩的,所以我們求它的逆矩陣會報錯。

除了這些函數之外,linalg當中還封裝了其他一些常用的函數。比如進行qr分解的qr函數,進行奇異值分解的svd函數,求解線性方程組的solve函數等。相比之下,這些函數的使用頻率相對不高,所以就不展開一一介紹了,我們可以用到的時候再去詳細研究。

隨機

Numpy當中另外一個常用的領域就是隨機數,我們經常使用Numpy來生成各種各樣的隨機數。這一塊在Numpy當中其實也有很多的api以及很複雜的用法,同樣,我們不過多深入,挑其中比較重要也是經常使用的和大家分享一下。

隨機數的所有函數都在numpy.random這個路徑下,我們為了簡化,就不寫完整的路徑了,大家記住就好。

randn

這個函數我們經常在代碼當中看到,尤其是我們造數據的時候。它代表的是根據輸入的shape生成一批均值為0,標準差為1的正態分佈的隨機數。

要注意的是,我們傳入的shape不是一個元組,而是每一維的大小,這一點和其他地方的用法不太一樣,需要注意一下。除了正態分佈的randn之外,還有均勻分佈的uniform和Gamma分佈的gamma,卡方分佈的chisquare。

normal

normal其實也是生成正態分佈的樣本值,但不同的是,它支持我們指定樣本的均值和標準差。如果我們想要生成多個樣本,還可以在size參數當中傳入指定的shape。

randint

顧名思義,這個函數是用來生成隨機整數的。它接受傳入隨機數的上下界,最少也要傳入一個上界(默認下界是0)。

如果想要生成多個int,我們可以在size參數傳入一個shape,它會返回一個對應大小的數組,這一點和uniform用法一樣。

shuffle

shuffle的功能是對一個數組進行亂序,返回亂序之後的結果。一般用在機器學習當中,如果存在樣本聚集的情況,我們一般會使用shuffle進行亂序,避免模型受到樣本分佈的影響。

shuffle是一個inplace的方法,它會在原本值上進行改動,而不會返回一個新值。

choice

這也是一個非常常用的api,它可以在數據當中抽取指定條數據。

但是它只支持一維的數組,一般用在批量訓練的時候,我們通過choice採樣出樣本的下標,再通過數組索引去找到這些樣本的值。比如這樣:

總結

今天我們一起研究了Numpy中數據持久化、線性代數、隨機數相關api的使用方法,由於篇幅的限制,我們只是選擇了其中比較常用,或者是比較重要的用法,還存在一些較為冷門的api和用法,大家感興趣的可以自行研究一下,一般來說文章當中提到的用法已經足夠了。

今天這篇是Numpy專題的最後一篇了,如果你堅持看完本專題所有的文章,那麼相信你對於Numpy包一定有了一個深入的理解和認識了,給自己鼓鼓掌吧。之後周四會開啟Pandas專題,敬請期待哦。

如果喜歡本文,可以的話,請點個關注,給我一點鼓勵,也方便獲取更多文章。

本文使用 mdnice 排版

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

分類
發燒車訊

JAVA設計模式 2【創建型】原型模式的理解與使用、理解淺克隆和深克隆

在本節中,我們將學習和使用原型模式;這一節學習的原型模式也是創建型 模式的其中之一。再次複習一下:創建型 模式就是描述如何去更好的創建一個對象。

我們都知道,在JAVA 語言中。使用new 關鍵字創建一個新對象。將新的對象放到堆內存 裏面。當然,這個內存肯定是有大小限制的,況且,JAVA 不同於C語言等。 有內存管理機制,就是我們常說的垃圾回收器GC,才可以保證內存不被溢出。

說這些其實就是為了表示:為啥要用單例模式,能節省內存的時候,能用一個對象解決重複的事情,絕對不會創建多個。

概述

原型模式描述的如何快速創建重複的對象,並且減少new 關鍵字的使用。

  • 抽象原型類
  • 具體原型類
  • 訪問類

容我來一個一個解釋:

抽象原型類 也就是我們具體要實現的某個類,這個類在JAVA 裏面是有具體的接口的,其實是一個空接口,Cloneable

 * @author  unascribed
 * @see     java.lang.CloneNotSupportedException
 * @see     java.lang.Object#clone()
 * @since   JDK1.0
 */
public interface Cloneable {
}

我們會發現,這個類沒有任何的方法,怎麼來實現它,不要慌。先接着走。

具體原型類 也就是我們具體要克隆 的對象。比如我們重複的要創建100個學生Student 對象,那麼具體的學生對象就是具體原型類

public class Student implements Cloneable {

    private int id;

    private String name;

    private int sex;
}

訪問類 我就不必多說了

淺克隆和深克隆

原型模式其實也分淺克隆和深克隆。如何理解這兩個概念呢?

淺克隆

protected native Object clone() throws CloneNotSupportedException;

淺克隆,只需要具體原型類 實現Cloneable 接口,並且重寫父類Object類的clone() 方法,即可實現對象的淺克隆。

Student student1 = new Student(1, "李四");
Student student2 = student1.clone();

System.out.println(student1);
System.out.println(student2);

System.out.println(student1 == student2);
---------------------
學號:1,姓名:李四
學號:1,姓名:李四
false
  • 通過執行clone() 方法即可創建一個相同的,具有同樣屬性的對象。
  • 並且是新的對象,內存地址有所不同。

我們來看看,對於引用類型的變量,淺克隆是否可以進行克隆;

Teacher teacher = new Teacher(1, "張老師");

Student student1 = new Student(1, "李四", teacher);
Student student2 = student1.clone();

System.out.println(student1);
System.out.println(student2);

System.out.println(student1 == student2);
------------
學號:1,姓名:李四,老師=Teacher@1b6d3586
學號:1,姓名:李四,老師=Teacher@1b6d3586
false

我們發現,引用類型並沒有被克隆,也就是說:

特點

  • 淺克隆對於基本類型,可以進行完全的克隆,並且克隆的對象是一個新的對象
  • 但是對象裏面的引用,是無法被克隆的。

深克隆(序列化)

何謂序列化?

我們創建的都是保存在內存裏面的,只要被虛擬機GC進行回收,那麼這個對象的任何屬性都是消失,我們能不能找一個方法,將內存中這種對象的屬性以及對象的狀態通過某種東西保存下來,比如保存到數據庫,下次從數據庫將這個對象還原到內存裏面。 這就是序列化。

  • 序列化 內存對象->序列字符
  • 反序列化 序列字符->內存對象

請參考: https://baike.baidu.com/item/序列化/2890184

JAVA 序列化

 * @see java.io.Externalizable
 * @since   JDK1.1
 */
public interface Serializable {
}

JAVA 提供了一個空接口,其實這個接口和上面的Cloneable 一樣,都是一個空接口,其實這個空接口就是作為一種標識 你的對象實現了這個接口,JAVA 認為你的這個就可以被序列化 ,就是這麼簡單。

Teacher teacher = new Teacher(1, "張老師");

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream stream = new ObjectOutputStream(outputStream);

stream.writeObject(teacher);
System.out.println(Arrays.toString(outputStream.toByteArray()));
----------
[-84, -19, 0, 5, 115, 114, 0, 7, 84, 101, 97,。。。。。。

通過將對象序列化、其實也就是將內存中的對象轉化為二進制 字節數組

反序列化

Teacher teacher = new Teacher(1, "張老師");
System.out.println(teacher);

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream stream = new ObjectOutputStream(outputStream);

stream.writeObject(teacher);
System.out.println(Arrays.toString(outputStream.toByteArray()));

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(outputStream.toByteArray());
ObjectInputStream inputStream = new ObjectInputStream(byteArrayInputStream);

Teacher teacher1 = (Teacher) inputStream.readObject();
System.out.println(teacher1);
---------------
id=1,name=張老師
[-84, -19, 0, 5, 115, xxxxx,-127, -27, -72, -120]
id=1,name=張老師

通過序列化和反序列化,即可對象的深克隆

小結

這一節,在講述 原型模式的同時,將原有實現原型模式的clone() 淺克隆,延伸到深克隆這一概念。其實JAVA 的原型模式,實現起來較為簡單。但還是要按需要實現,Object 類提供的 clone 淺克隆 是沒辦法克隆對象的引用類型的。需要克隆引用類型,還是需要序列化 深克隆

參考

http://c.biancheng.net/view/1343.html
https://www.liaoxuefeng.com/wiki/1252599548343744/1298366845681698

代碼示例

https://gitee.com/mrc1999/Dev-Examples

歡迎關注

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧