標籤: 潭子電動車

  • 用廢棄物鋪路 越南首條回收塑膠道路啟用

    摘錄自2019年10月3日中央社報導

    越南第一條用廢棄塑膠鋪成的200公尺長道路日前在海防市啟用。這項以廢棄塑膠鋪路的提案由DOW CHEMICAL VIETNAM公司、海防市(Hai Phong)廷武(Dinh Vu)工業區(Deep C)開發商與越南航海大學等單位合作展開,其中第一階段在這個工業區內完成興建以廢棄塑膠鋪成的200公尺長道路。

    越南媒體報導,大量回收塑膠廢棄物由當地環保單位提供,經清洗、烘乾與壓碎後,在攝氏150至180度的溫度下與瀝青混合,隨後用來鋪路;這個塑膠和瀝青混合的材料,有利提升道路耐用度。

    越南商工總會(VCCI)表示,塑膠廢棄物處理的新出路符合於越南政府所提出經濟永續發展政策以及「全國反對塑膠垃圾行動」,這個模式應在各其他地方擴大實施。

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

    【其他文章推薦】

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

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

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

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

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

  • 全球60城市氣候抗議行動開跑 紐澳數十人被捕 阿姆斯特丹數百人阻交通

    摘錄自2019年10月7日聯合報報導

    「反抗滅絕」在全球發起公民不服從運動7日展開,在澳洲和紐西蘭各地有數十名示威者遭到逮捕。這場運動將為期2週,目的為了要求各國政府採取緊急行動對抗氣候變遷。

    數百人在繁忙的雪梨市中心道路上靜坐封路,最後警察拖走示威者終止這場活動,隨後有30人遭到起訴。

    在布里斯本,一小群運動人士把自己鎖在一座橋上。警方表示,他們在現場逮捕7人,並全數加以起訴。

    同一時間在紐西蘭首都威靈頓,示威者用鎖鏈把自己與一輛閃亮的粉紅色車輛綁住,造成市區部分交通癱瘓。警方表示,今天稍晚有30人遭到逮捕,但無人被起訴。

    荷蘭阿姆斯特丹市7日出現數百名氣候變遷倡議人士,他們無視警方的禁令,高喊「反抗」,阻礙市中心交通。警方已警告在先,將逮捕違令人士,防止他們擾亂通勤民眾。

    荷蘭警方已核准倡議團體「反抗滅絕」(Extinction Rebellion, XR)的抗議計劃,前提是不能阻礙交通。

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

    【其他文章推薦】

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

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

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

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

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

  • MongoDB via Dotnet Core數據映射詳解

    MongoDB via Dotnet Core數據映射詳解

    用好數據映射,MongoDB via Dotnet Core開發變會成一件超級快樂的事。

    一、前言

    MongoDB這幾年已經成為NoSQL的頭部數據庫。

    由於MongoDB free schema的特性,使得它在互聯網應用方面優於常規數據庫,成為了相當一部分大廠的主數據選擇;而它的快速布署和開發簡單的特點,也吸引着大量小開發團隊的支持。

    關於MongoDB快速布署,我在15分鐘從零開始搭建支持10w+用戶的生產環境(二)里有寫,需要了可以去看看。

    作為一個數據庫,基本的操作就是CRUD。MongoDB的CRUD,不使用SQL來寫,而是提供了更簡單的方式。

    方式一、BsonDocument方式

    BsonDocument方式,適合能熟練使用MongoDB Shell的開發者。MongoDB Driver提供了完全覆蓋Shell命令的各種方式,來處理用戶的CRUD操作。

    這種方法自由度很高,可以在不需要知道完整數據集結構的情況下,完成數據庫的CRUD操作。

    方式二、數據映射方式

    數據映射是最常用的一種方式。準備好需要處理的數據類,直接把數據類映射到MongoDB,並對數據集進行CRUD操作。

    下面,對數據映射的各個部分,我會逐個說明。

        為了防止不提供原網址的轉載,特在這裏加上原文鏈接:https://www.cnblogs.com/tiger-wang/p/13185605.html

    二、開發環境&基礎工程

    這個Demo的開發環境是:Mac + VS Code + Dotnet Core 3.1.2。

    建立工程:

    % dotnet new sln -o demo
    The template "Solution File" was created successfully.
    cd demo 
    % dotnet new console -o demo
    The template "Console Application" was created successfully.

    Processing post-creation actions...
    Running 'dotnet restore' on demo/demo.csproj...
      Determining projects to restore...
      Restored demo/demo/demo.csproj (in 162 ms).

    Restore succeeded.
    % dotnet sln add demo/demo.csproj 
    Project `demo/demo.csproj` added to the solution.

    建立工程完成。

    下面,增加包mongodb.driver到工程:

    cd demo
    % dotnet add package mongodb.driver
      Determining projects to restore...
    info : Adding PackageReference for package 'mongodb.driver' into project 'demo/demo/demo.csproj'.
    info : Committing restore...
    info : Writing assets file to disk. Path: demo/demo/obj/project.assets.json
    log  : Restored /demo/demo/demo.csproj (in 6.01 sec).

    項目準備完成。

    看一下目錄結構:

    % tree .
    .
    ├── demo
    │   ├── Program.cs
    │   ├── demo.csproj
    │   └── obj
    │       ├── demo.csproj.nuget.dgspec.json
    │       ├── demo.csproj.nuget.g.props
    │       ├── demo.csproj.nuget.g.targets
    │       ├── project.assets.json
    │       └── project.nuget.cache
    └── demo.sln

    mongodb.driver是MongoDB官方的數據庫SDK,從Nuget上安裝即可。

    三、Demo準備工作

    創建數據映射的模型類CollectionModel.cs,現在是個空類,後面所有的數據映射相關內容會在這個類進行說明:

    public class CollectionModel
    {

    }

    並修改Program.cs,準備Demo方法,以及連接數據庫:

    class Program
    {

        private const string MongoDBConnection = "mongodb://localhost:27031/admin";

        private static IMongoClient _client = new MongoClient(MongoDBConnection);
        private static IMongoDatabase _database = _client.GetDatabase("Test");
        private static IMongoCollection<CollectionModel> _collection = _database.GetCollection<CollectionModel>("TestCollection");

        static async Task Main(string[] args)
        
    {
            await Demo();
            Console.ReadKey();
        }

        private static async Task Demo()
        
    {
        }
    }

    四、字段映射

    從上面的代碼中,我們看到,在生成Collection對象時,用到了CollectionModel

    IMongoDatabase _database = _client.GetDatabase("Test");
    IMongoCollection<CollectionModel> _collection = _database.GetCollection<CollectionModel>("TestCollection");

    這兩行,其實就完成了一個映射的工作:把MongoDB中,Test數據庫下,TestCollection數據集(就是SQL中的數據表),映射到CollectionModel這個數據類中。換句話說,就是用CollectionModel這個類,來完成對數據集TestCollection的所有操作。

    保持CollectionModel為空,我們往數據庫寫入一行數據:

    private static async Task Demo()
    {
        CollectionModel new_item = new CollectionModel();
        await _collection.InsertOneAsync(new_item);
    }

    執行,看一下寫入的數據:


        "_id" : ObjectId("5ef1d8325327fd4340425ac9")
    }

    OK,我們已經寫進去一條數據了。因為映射類是空的,所以寫入的數據,也只有_id一行內容。

    但是,為什麼會有一個_id呢?

    1. ID字段

    MongoDB數據集中存放的數據,稱之為文檔(Document)。每個文檔在存放時,都需要有一個ID,而這個ID的名稱,固定叫_id

    當我們建立映射時,如果給出_id字段,則MongoDB會採用這個ID做為這個文檔的ID,如果不給出,MongoDB會自動添加一個_id字段。

    例如:

    public class CollectionModel
    {

        public ObjectId _id { get; set; }
        public string title { get; set; }
        public string content { get; set; }
    }

    public class CollectionModel
    {

        public string title { get; set; }
        public string content { get; set; }
    }

    在使用上是完全一樣的。唯一的區別是,如果映射類中不寫_id,則MongoDB自動添加_id時,會用ObjectId作為這個字段的數據類型。

    ObjectId是一個全局唯一的數據。

    當然,MongoDB允許使用其它類型的數據作為ID,例如:stringintlongGUID等,但這就需要你自己去保證這些數據不超限並且唯一。

    例如,我們可以寫成:

    public class CollectionModel
    {

        public long _id { get; set; }
        public string title { get; set; }
        public string content { get; set; }
    }

    我們也可以在類中修改_id名稱為別的內容,但需要加一個描述屬性BsonId

    public class CollectionModel
    {

        [BsonId]
        public ObjectId topic_id { get; set; }
        public string title { get; set; }
        public string content { get; set; }
    }

    這兒特別要注意:BsonId屬性會告訴映射,topic_id就是這個文檔數據的ID。MongoDB在保存時,會將這個topic_id轉成_id保存到數據集中。

    在MongoDB數據集中,ID字段的名稱固定叫_id。為了代碼的閱讀方便,可以在類中改為別的名稱,但這不會影響MongoDB中存放的ID名稱。

    修改Demo代碼:

    private static async Task Demo()
    {
        CollectionModel new_item = new CollectionModel()
        {
            title = "Demo",
            content = "Demo content",
        };
        await _collection.InsertOneAsync(new_item);
    }

    跑一下Demo,看看保存的結果:


        "_id" : ObjectId("5ef1e1b1bc1e18086afe3183"), 
        "title" : "Demo"
        "content" : "Demo content"
    }

    2. 簡單字段

    就是常規的數據字段,直接寫就成。

    public class CollectionModel
    {

        [BsonId]
        public ObjectId topic_id { get; set; }
        public string title { get; set; }
        public string content { get; set; }
        public int favor { get; set; }
    }

    保存后的數據:


        "_id" : ObjectId("5ef1e9caa9d16208de2962bb"), 
        "title" : "Demo"
        "content" : "Demo content"
        "favor" : NumberInt(100)
    }

    3. 一個的特殊的類型 – Decimal

    說Decimal特殊,是因為MongoDB在早期,是不支持Decimal的。直到MongoDB v3.4開始,數據庫才正式支持Decimal。

    所以,如果使用的是v3.4以後的版本,可以直接使用,而如果是以前的版本,需要用以下的方式:

    [BsonRepresentation(BsonType.Double, AllowTruncation = true)]
    public decimal price { get; set; }

    其實就是把Decimal通過映射,轉為Double存儲。

    4. 類字段

    把類作為一個數據集的一個字段。這是MongoDB作為文檔NoSQL數據庫的特色。這樣可以很方便的把相關的數據組織到一條記錄中,方便展示時的查詢。

    我們在項目中添加兩個類ContactAuthor

    public class Contact
    {

        public string mobile { get; set; }
    }
    public class Author
    {

        public string name { get; set; }
        public List<Contact> contacts { get; set; }
    }

    然後,把Author加到CollectionModel中:

    public class CollectionModel
    {

        [BsonId]
        public ObjectId topic_id { get; set; }
        public string title { get; set; }
        public string content { get; set; }
        public int favor { get; set; }
        public Author author { get; set; }
    }

    嗯,開始變得有點複雜了。

    完善Demo代碼:

    private static async Task Demo()
    {
        CollectionModel new_item = new CollectionModel()
        {
            title = "Demo",
            content = "Demo content",
            favor = 100,
            author = new Author
            {
                name = "WangPlus",
                contacts = new List<Contact>(),
            }
        };

        Contact contact_item1 = new Contact()
        {
            mobile = "13800000000",
        };
        Contact contact_item2 = new Contact()
        {
            mobile = "13811111111",
        };
        new_item.author.contacts.Add(contact_item1);
        new_item.author.contacts.Add(contact_item2);

        await _collection.InsertOneAsync(new_item);
    }

    保存的數據是這樣的:


        "_id" : ObjectId("5ef1e635ce129908a22dfb5e"), 
        "title" : "Demo"
        "content" : "Demo content"
        "favor" : NumberInt(100),
        "author" : {
            "name" : "WangPlus"
            "contacts" : [
                {
                    "mobile" : "13800000000"
                }, 
                {
                    "mobile" : "13811111111"
                }
            ]
        }
    }

    這樣的數據結構,用着不要太爽!

    5. 枚舉字段

    枚舉字段在使用時,跟類字段相似。

    創建一個枚舉TagEnumeration

    public enum TagEnumeration
    {
        CSharp = 1,
        Python = 2,
    }

    加到CollectionModel中:

    public class CollectionModel
    {

        [BsonId]
        public ObjectId topic_id { get; set; }
        public string title { get; set; }
        public string content { get; set; }
        public int favor { get; set; }
        public Author author { get; set; }
        public TagEnumeration tag { get; set; }
    }

    修改Demo代碼:

    private static async Task Demo()
    {
        CollectionModel new_item = new CollectionModel()
        {
            title = "Demo",
            content = "Demo content",
            favor = 100,
            author = new Author
            {
                name = "WangPlus",
                contacts = new List<Contact>(),
            },
            tag = TagEnumeration.CSharp,
        };
        /* 後邊代碼略過 */
    }

    運行后看數據:


        "_id" : ObjectId("5ef1eb87cbb6b109031fcc31"), 
        "title" : "Demo"
        "content" : "Demo content"
        "favor" : NumberInt(100), 
        "author" : {
            "name" : "WangPlus"
            "contacts" : [
                {
                    "mobile" : "13800000000"
                }, 
                {
                    "mobile" : "13811111111"
                }
            ]
        }, 
        "tag" : NumberInt(1)
    }

    在這裏,tag保存了枚舉的值。

    我們也可以保存枚舉的字符串。只要在CollectionModel中,tag聲明上加個屬性:

    public class CollectionModel
    {

        [BsonId]
        public ObjectId topic_id { get; set; }
        public string title { get; set; }
        public string content { get; set; }
        public int favor { get; set; }
        public Author author { get; set; }
        [BsonRepresentation(BsonType.String)]
        public TagEnumeration tag { get; set; }
    }

    數據會變成:


        "_id" : ObjectId("5ef1ec448f1d540919d15904"), 
        "title" : "Demo"
        "content" : "Demo content"
        "favor" : NumberInt(100), 
        "author" : {
            "name" : "WangPlus"
            "contacts" : [
                {
                    "mobile" : "13800000000"
                }, 
                {
                    "mobile" : "13811111111"
                }
            ]
        }, 
        "tag" : "CSharp"
    }

    6. 日期字段

    日期字段會稍微有點坑。

    這個坑其實並不源於MongoDB,而是源於C#的DateTime類。我們知道,時間根據時區不同,時間也不同。而DateTime並不準確描述時區的時間。

    我們先在CollectionModel中增加一個時間字段:

    public class CollectionModel
    {

        [BsonId]
        public ObjectId topic_id { get; set; }
        public string title { get; set; }
        public string content { get; set; }
        public int favor { get; set; }
        public Author author { get; set; }
        [BsonRepresentation(BsonType.String)]
        public TagEnumeration tag { get; set; }
        public DateTime post_time { get; set; }
    }

    修改Demo:

    private static async Task Demo()
    {
        CollectionModel new_item = new CollectionModel()
        {
            /* 前邊代碼略過 */
            post_time = DateTime.Now, /* 2020-06-23T20:12:40.463+0000 */
        };
        /* 後邊代碼略過 */
    }

    運行看數據:


        "_id" : ObjectId("5ef1f1b9a75023095e995d9f"), 
        "title" : "Demo"
        "content" : "Demo content"
        "favor" : NumberInt(100), 
        "author" : {
            "name" : "WangPlus"
            "contacts" : [
                {
                    "mobile" : "13800000000"
                }, 
                {
                    "mobile" : "13811111111"
                }
            ]
        }, 
        "tag" : "CSharp"
        "post_time" : ISODate("2020-06-23T12:12:40.463+0000")
    }

    對比代碼時間和數據時間,會發現這兩個時間差了8小時 – 正好的中國的時區時間。

    MongoDB規定,在數據集中存儲時間時,只會保存UTC時間。

    如果只是保存(像上邊這樣),或者查詢時使用時間作為條件(例如查詢post_time < DateTime.Now的數據)時,是可以使用的,不會出現問題。

    但是,如果是查詢結果中有時間字段,那這個字段,會被DateTime默認設置為DateTimeKind.Unspecified類型。而這個類型,是無時區信息的,輸出显示時,會造成混亂。

    為了避免這種情況,在進行時間字段的映射時,需要加上屬性:

    [BsonDateTimeOptions(Kind = DateTimeKind.Local)]
    public DateTime post_time { get; set; }

    這樣做,會強制DateTime類型的字段為DateTimeKind.Local類型。這時候,從显示到使用就正確了。

    但是,別高興的太早,這兒還有一個但是。

    這個但是是這樣的:數據集中存放的是UTC時間,跟我們正常的時間有8小時時差,如果我們需要按日統計,比方每天的銷售額/點擊量,怎麼搞?上面的方式,解決不了。

    當然,基於MongoDB自由的字段處理,可以把需要統計的字段,按年月日時分秒拆開存放,像下面這樣的:

    class Post_Time
    {

        public int year { get; set; }
        public int month { get; set; }
        public int day { get; set; }
        public int hour { get; set; }
        public int minute { get; set; }
        public int second { get; set; }
    }

    能解決,但是Low哭了有沒有?

    下面,終極方案來了。它就是:改寫MongoDB中對於DateTime字段的序列化類。噹噹當~~~

    先創建一個類MyDateTimeSerializer

    public class MyDateTimeSerializer : DateTimeSerializer
    {
        public override DateTime Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
        
    {
            var obj = base.Deserialize(context, args);
            return new DateTime(obj.Ticks, DateTimeKind.Unspecified);
        }
        public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, DateTime value)
        
    {
            var utcValue = new DateTime(value.Ticks, DateTimeKind.Utc);
            base.Serialize(context, args, utcValue);
        }
    }

    代碼簡單,一看就懂。

    注意,使用這個方法,上邊那個對於時間加的屬性[BsonDateTimeOptions(Kind = DateTimeKind.Local)]一定不要添加,要不然就等着哭吧:P

    創建完了,怎麼用?

    如果你只想對某個特定映射的特定字段使用,比方只對CollectionModelpost_time字段來使用,可以這麼寫:

    [BsonSerializer(typeof(MyDateTimeSerializer))]
    public DateTime post_time { get; set; }

    或者全局使用:

    BsonSerializer.RegisterSerializer(typeof(DateTime), new MongoDBDateTimeSerializer());

    BsonSerializer是MongoDB.Driver的全局對象。所以這個代碼,可以放到使用數據庫前的任何地方。例如在Demo中,我放在Main里了:

    static async Task Main(string[] args)
    {
        BsonSerializer.RegisterSerializer(typeof(DateTime), new MyDateTimeSerializer());

        await Demo();
        Console.ReadKey();
    }

    這回看數據,數據集中的post_time跟當前時間显示完全一樣了,你統計,你分組,可以隨便霍霍了。

    7. Dictionary字段

    這個需求很奇怪。我們希望在一個Key-Value的文檔中,保存一個Key-Value的數據。但這個需求又是真實存在的,比方保存一個用戶的標籤和標籤對應的命中次數。

    數據聲明很簡單:

    public Dictionary<stringint> extra_info { get; set; }

    MongoDB定義了三種保存屬性:DocumentArrayOfDocumentsArrayOfArrays,默認是Document

    屬性寫法是這樣的:

    [BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)]
    public Dictionary<stringint> extra_info { get; set; }

    這三種屬性下,保存在數據集中的數據結構有區別。

    DictionaryRepresentation.Document


        "extra_info" : {
            "type" : NumberInt(1), 
            "mode" : NumberInt(2)
        }
    }

    DictionaryRepresentation.ArrayOfDocuments


        "extra_info" : [
            {
                "k" : "type"
                "v" : NumberInt(1)
            }, 
            {
                "k" : "mode"
                "v" : NumberInt(2)
            }
        ]
    }

    DictionaryRepresentation.ArrayOfArrays


        "extra_info" : [
            [
                "type"
                NumberInt(1)
            ], 
            [
                "mode"
                NumberInt(2)
            ]
        ]
    }

    這三種方式,從數據保存上並沒有什麼區別,但從查詢來講,如果這個字段需要進行查詢,那三種方式區別很大。

    如果採用BsonDocument方式查詢,DictionaryRepresentation.Document無疑是寫着最方便的。

    如果用Builder方式查詢,DictionaryRepresentation.ArrayOfDocuments是最容易寫的。

    DictionaryRepresentation.ArrayOfArrays就算了。數組套數組,查詢條件寫死人。

    我自己在使用時,多數情況用DictionaryRepresentation.ArrayOfDocuments

    五、其它映射屬性

    上一章介紹了數據映射的完整內容。除了這些內容,MongoDB還給出了一些映射屬性,供大家看心情使用。

    1. BsonElement屬性

    這個屬性是用來改數據集中的字段名稱用的。

    看代碼:

    [BsonElement("pt")]
    public DateTime post_time { get; set; }

    在不加BsonElement的情況下,通過數據映射寫到數據集中的文檔,字段名就是變量名,上面這個例子,字段名就是post_time

    加上BsonElement后,數據集中的字段名會變為pt

    2. BsonDefaultValue屬性

    看名稱就知道,這是用來設置字段的默認值的。

    看代碼:

    [BsonDefaultValue("This is a default title")]
    public string title { get; set; }

    當寫入的時候,如果映射中不傳入值,則數據庫會把這個默認值存到數據集中。

    3. BsonRepresentation屬性

    這個屬性是用來在映射類中的數據類型和數據集中的數據類型做轉換的。

    看代碼:

    [BsonRepresentation(BsonType.String)]
    public int favor { get; set; }

    這段代表表示,在映射類中,favor字段是int類型的,而存到數據集中,會保存為string類型。

    前邊Decimal轉換和枚舉轉換,就是用的這個屬性。

    4. BsonIgnore屬性

    這個屬性用來忽略某些字段。忽略的意思是:映射類中某些字段,不希望被保存到數據集中。

    看代碼:

    [BsonIgnore]
    public string ignore_string { get; set; }

    這樣,在保存數據時,字段ignore_string就不會被保存到數據集中。

    六、總結

    數據映射本身沒什麼新鮮的內容,但在MongoDB中,如果用好了映射,開發過程從效率到爽的程度,都不是SQL可以相比的。正所謂:

    一入Mongo深似海,從此SQL是路人。

    謝謝大家!

    (全文完)

    本文的配套代碼在https://github.com/humornif/Demo-Code/tree/master/0015/demo

     

     

    微信公眾號:老王Plus

    掃描二維碼,關注個人公眾號,可以第一時間得到最新的個人文章和內容推送

    本文版權歸作者所有,轉載請保留此聲明和原文鏈接

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

    【其他文章推薦】

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

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

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

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

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

  • Tesla Model 3 有對手了!LG Chem 研發長程電動車電池

    Tesla 又有新對手了,南韓 LG Chem 目前也在開發最新電池技術,且無論航程或售價均足以與特斯拉研發中的 Model 3 匹敵。   Model S 是目前市面上唯一航程可以達 200 英哩的電動車,但即使是最低售價也高達 7.1 萬美元。特斯拉計畫在 2017 年推出 Model 3,售價將壓低至每輛 3.5 萬美元,但售價仍然不敵 LG Chem 正將推出的電動車。   LG Chem 位於美國的研究單位主管 Prabhakar Patil 證實,正在開發續航力 200 英哩的電池,目前已有幾家汽車業者表示對該項技術有興趣,產品最快於 2017 年就可上市,售價將介於 3 至 3.5 萬美元間。   Patil 雖然沒有明講,但外界猜想對該項技術有興趣的汽車業者,一定包含通用汽車。LG Chem 目前已是通用油電混合動力車 Volt 的電池供應商,且通用之前表示,將推出續航力 200 英哩的電動車,起價最低 3 萬美元,符合 Patil 設定的條件。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

  • 跟着whatwg看一遍事件循環

    跟着whatwg看一遍事件循環

    前言

    對於單線程來說,事件循環可以說是重中之重了,它為任務分配不同的優先級,井然有序的調度。讓js解析,用戶交互,頁面渲染等互不衝突,各司其職。

    我們書寫的代碼無時無刻都在和事件循環打交道,要想寫出更流暢,我們就必須深入了解事件循環,下面我們將從規範中翻譯和解讀整個流程。

    以下內容來自whatwg文檔,均為個人理解,若有不對,煩請指出,我會第一時間修改,避免誤導他人!

    正文

    為了協調用戶操作,js執行,頁面渲染,網絡請求等事件,每個宿主中,存在事件循環這樣的角色,並且該角色在當前宿主中是唯一的。

    簡單解釋一下宿主:宿主是一個ECMAScript執行上下文,一般包含執行上下文棧,運行時執行環境,宿主記錄和一個執行線程,除了這個執行線程外,其他的專屬於當前宿主。例如,某些瀏覽器在不同的tabs使用同一個執行線程。

    不僅如此,事件循環又存於在各個不同場景,有瀏覽器環境下的,worker環境下的和Worklet環境下的。

    Worklet是一個輕量級的web worker,可以讓開發者訪問更底層的渲染工作線,也就是說你可以通過Worklet去干預瀏覽器的渲染環境。

    提到了worklet,那就順便看一個例子(需開啟服務,不要以file協議運行),通過這個例子,可以看到事件循環不同階段觸發了什麼鈎子函數:

    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>Document</title>
            <style>
                .fancy {
                    background-image: paint(headerHighlight);
                    display: layout(sample-layout);
                    background-color: green;
                }
            </style>
        </head>
        <body>
            <h1 class="fancy">My Cool Header</h1>
            <script>
                console.log('開始');
                CSS.paintWorklet.addModule('./paint.js');
                CSS.layoutWorklet.addModule('./layout.js');
    
                requestAnimationFrame(() => {
                    console.log('requestAnimationFrame');
                });
                Promise.resolve().then(() => {
                    console.log('微任務');
                });
                setTimeout(function () {
                    document.querySelector('.fancy').style.height = '150px';
                    ('translateZ(0)');
    
                    Promise.resolve().then(() => {
                        console.log('新一輪的微任務');
                    });
                    requestAnimationFrame(() => {
                        console.log('新一輪的requestAnimationFrame');
                    });
                }, 2000);
                console.log(2);
            </script>
        </body>
    </html>
    
    
    // paint.js
    registerPaint(
        'headerHighlight',
        class {
            static get contextOptions() {
                console.log('contextOptions');
                return {alpha: true};
            }
    
            paint(ctx) {
                console.log('paint函數');
            }
        }
    );
    
    // ==========================分割線
    
    // layout.js
    registerLayout(
        'sample-layout',
        class {
            async intrinsicSizes(children, edges, styleMap) {}
    
            async layout(children, edges, constraints, styleMap, breakToken) {
                console.log('layout階段');
            }
        }
    );
    

    事件循環有一個或多個Task隊列,每個Task隊列都是Task的一個集合。其中Task不是指我們的某個函數,而是一個上下文環境,結構如下:

    • step:一系列任務將要執行的步驟
    • source:任務來源,常用來對相關任務進行分組和系列化
    • document:與當前任務相關的document對象,如果是非window環境則為null
    • 環境配置對象:在任務期間追蹤記錄任務狀態

    這裏的Task隊列不是Task,是一個集合,因為取出一個Task隊列中的Task是選擇一個可執行的Task,而不是出隊操作。

    微任務隊列是一個入對出對的隊列。

    這裏說明一下,Task隊列為什麼有多個,因為不同的Task隊列有不同的優先級,進而進行次序排列和調用,有沒有感覺react的fiber和這個有點類似?

    舉個例子,Task隊列可以是專門負責鼠標和鍵盤事件的,並且賦予鼠標鍵盤隊列較高的優先級,以便及時響應用戶操作。另一個Task隊列負責其他任務源。不過也不要餓死任何一個task,這個後續處理模型中會介紹。

    Task封裝了負責以下任務的算法:

    • Events: 由專門的Task在特定的EventTarget(一個具有監聽訂閱模式列表的對象)上分發事件對象
    • Parsing: html解析器標記一個或多個字節,並處理所有生成的結果token
    • Callbacks: 由專門的Task觸發回調函數
    • Using a resource: 當該算法獲取資源的時候,如果該階段是以非阻塞方式發生,那麼一旦部分或者全部資源可用,則由Task進行後續處理
    • Reacting to DOM manipulation: 通過dom操作觸發的任務,例如插入一個節點到document

    事件循環有一個當前運行中的Task,可以為null,如果是null的話,代表着可以接受一個新的Task(新一輪的步驟)。

    事件循環有微任務隊列,默認為空,其中的任務由微任務排隊算法創建。

    事件循環有一個執行微任務檢查點,默認為false,用來防止微任務死循環。

    微任務排隊算法:

    1. 如果未提供event loop,設置一個隱式event loop。
    2. 如果未提供document,設置一個隱式document.
    3. 創建一個Task作為新的微任務
    4. 設置setp、source、document到新的Task上
    5. 設置Task的環境配置對象為空集
    6. 添加到event loop的微任務隊列中

    微任務檢查算法:

    1. 如果微任務檢查標誌為true,直接return
    2. 設置微任務檢查標誌為true
    3. 如果微任務隊里不為空(也就是說微任務添加的微任務也會在這個循環中出現,直到微任務隊列為空):
      1. 從微任務隊列中找出最老的任務(防餓死)
      2. 設置當前執行任務為這個最老的任務
      3. 執行
      4. 重置當前執行任務為null
    4. 通知環境配置對象的promise進行reject操作
    5. 清理indexdb事務(不太明白這一步,如果有讀者了解,煩請點撥一下)
    6. 設置微任務檢查標誌為false

    處理模型

    event loop會按照下面這些步驟進行調度:

    1. 找到一個可執行的Task隊列,如果沒有則跳轉到下面的微任務步驟
    2. 讓最老的Task作為Task隊列中第一個可執行的Task,並將其移除
    3. 將最老的Task作為event loop的可執行Task
    4. 記錄任務開始時間點
    5. 執行Task中的setp對應的步驟(上文中Task結構中的step)
    6. 設置event loop的可執行任務為null
    7. 執行微任務檢查算法
    8. 設置hasARenderingOpportunity(是否可以渲染的flag)為false
    9. 記住當前時間點
    10. 通過下面步驟記錄任務持續時間
      1. 設置頂層瀏覽器環境為空
      2. 對於每個最老Task的腳本執行環境配置對象,設置當前的頂級瀏覽器上下文到其上
      3. 報告消耗過長的任務,並附帶開始時間,結束時間,頂級瀏覽器上下文和當前Task
    11. 如果在window環境下,會根據硬件條件決定是否渲染,比如刷新率,頁面性能,頁面是否在後台,不過渲染會定期出現,避免頁面卡頓。值得注意的是,正常的刷新率為60hz,大概是每秒60幀,大約16.7ms每幀,如果當前瀏覽器環境不支持這個刷新率的話,會自動降為30hz,而不是丟幀。而李蘭其在後台的時候,聰明的瀏覽器會將這個渲染時機降為每秒4幀甚至更低,事件循環也會減少(這就是為什麼我們可以用setInterval來判斷時候能打開其他app的判斷依據的原因)。如果能渲染的話會設置hasARenderingOpportunity為true。

    除此之外,還會在觸發resize、scroll、建立媒體查詢、運行css動畫等,也就是說瀏覽器幾乎大部分用戶操作都發生在事件循環中,更具體點是事件循環中的ui render部分。之後會進行requestAnimationFrame和IntersectionObserver的觸發,再之後是ui渲染

    1. 如果下麵條件都成立,那麼執行空閑階段算法,對於開發者來說就是調用window.requestIdleCallback方法
      1. 在window環境下
      2. event loop中沒有活躍的Task
      3. 微任務隊列為空
      4. hasARenderingOpportunity為false

    借鑒網上的一張圖來粗略表示下整個流程

    小結

    上面就是整個事件循環的流程,瀏覽器就是按照這個規則一遍遍的執行,而我們要做的就是了解並適應這個規則,讓瀏覽器渲染出性能更高的頁面。

    比如:

    1. 非首屏相關性能打點可以放到idle callback中執行,減少對頁面性能的損耗
    2. 微任務中遞歸添加微任務會導致頁面卡死,而不是隨着事件循環一輪輪的執行
    3. 更新元素布局的最好時機是在requestAnimateFrame中
    4. 盡量避免頻繁獲取元素布局信息,因為這會觸發強制layout(哪些屬性會導致強制layout?),影響頁面性能
    5. 事件循環有多個任務隊列,他們互不衝突,但是用戶交互相關的優先級更高
    6. resize、scroll等會伴隨事件循環中ui渲染觸發,而不是根據我們的滾動觸發,換句話說,這些操作自帶節流
    7. 等等,歡迎補充

    最後感謝大家閱讀,歡迎一起探討!

    提前祝大家端午節nb

    參考

    composite

    深入探究 eventloop 與瀏覽器渲染的時序問題

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

    【其他文章推薦】

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

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

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

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

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

  • 用Map+函數式接口來實現策略模式

    用Map+函數式接口來實現策略模式

    用Map+函數式接口來實現策略模式

    目前在魔都,貝殼找房是我的僱主,平時關注一些 java 領域相關的技術,希望你們能在這篇文章中找到些有用的東西。個人水平有限,如果文章有錯誤還請指出,在留言區一起交流。

    本文已投稿至公眾號 Hollis 原文鏈接:https://mp.weixin.qq.com/s/hkypvNBkRjPM6HM51_jW9g

    我想大家肯定都或多或少的看過各種“策略模式”的講解、佈道等等,這篇文章就是來好好“澄清”一下策略模式,並嘗試回答以下的問題:

    1. 策略模式是如何優化業務邏輯代碼結構的?
    2. 殺雞焉用宰牛刀?就是幾個if else場景我需要用到策略模式?!
    3. 有沒有什麼更好的代碼結構來實現策略模式的嗎?

    策略模式是如何優化業務邏輯代碼結構的?

    要回答這個問題,我們還得先扒一扒策略模式的定義,從定義着手來理解它:

    策略模式的教科書定義

    它的定義很精簡:一個類的行為或其算法可以在運行時更改。我們把它降維到代碼層面,用人話翻譯一下就是,運行時我給你這個類的方法傳不同的“key”,你這個方法會執行不同的業務邏輯。細品一下,這不就是 if else 乾的事嗎?

    策略模式優化了什麼?

    其實策略模式的核心思想和 if else如出一轍,根據不同的key動態的找到不同的業務邏輯,那它就只是如此嗎?

    實際上,我們口中的策略模式其實就是在代碼結構上調整,用接口+實現類+分派邏輯來使代碼結構可維護性好點。

    一般教科書上講解到接口與實現類就結束了,其他博客上會帶上提及分派邏輯。這裏就不啰嗦了。

    小結一下,即使用了策略模式,你該寫的業務邏輯照常寫,到邏輯分派的時候,還是變相的if else。而它的優化點是抽象了出了接口,將業務邏輯封裝成一個一個的實現類,任意地替換。在複雜場景(業務邏輯較多)時比直接 if else 來的好維護些。

    殺雞焉用宰牛刀?就是幾個if else場景我需要用到策略模式?!

    我想小夥伴們經常有這樣的不滿,我的業務邏輯就3 4 行,你給我整一大堆類定義?有必要這麼麻煩嗎?我看具體的業務邏輯還需要去不同的類中,簡單點行不行。

    其實我們所不滿的就是策略模式帶來的缺點:1、策略類會增多 2、業務邏輯分散到各個實現類中,而且沒有一個地方可以俯視整個業務邏輯

    針對傳統策略模式的缺點,在這分享一個實現思路,這個思路已經幫我們團隊解決了多個複雜if else的業務場景,理解上比較容易,代碼上需要用到Java8的特性——利用Map與函數式接口來實現。

    直接show代碼結構:為了簡單演示一個思路,代碼用String 類型來模擬一個業務BO

    其中:

    1. getCheckResult() 為傳統的做法
    2. getCheckResultSuper()則事先在Map中定義好了“判斷條件”與“業務邏輯”的映射關係,具體講解請看代碼註釋
    /**
     * 某個業務服務類
     */
    @Service
    public class BizService {
    
        /**
         * 傳統的 if else 解決方法
         * 當每個業務邏輯有 3 4 行時,用傳統的策略模式不值得,直接的if else又顯得不易讀
         */
        public String getCheckResult(String order) {
            if ("校驗1".equals(order)) {
                return "執行業務邏輯1";
            } else if ("校驗2".equals(order)) {
                return "執行業務邏輯2";
            }else if ("校驗3".equals(order)) {
                return "執行業務邏輯3";
            }else if ("校驗4".equals(order)) {
                return "執行業務邏輯4";
            }else if ("校驗5".equals(order)) {
                return "執行業務邏輯5";
            }else if ("校驗6".equals(order)) {
                return "執行業務邏輯6";
            }else if ("校驗7".equals(order)) {
                return "執行業務邏輯7";
            }else if ("校驗8".equals(order)) {
                return "執行業務邏輯8";
            }else if ("校驗9".equals(order)) {
                return "執行業務邏輯9";
            }
            return "不在處理的邏輯中返回業務錯誤";
        }
    
        /**
         * 業務邏輯分派Map
         * Function為函數式接口,下面代碼中 Function<String, String> 的含義是接收一個Stirng類型的變量,返回一個String類型的結果
         */
        private Map<String, Function<String, String>> checkResultDispatcher = new HashMap<>();
    
        /**
         * 初始化 業務邏輯分派Map 其中value 存放的是 lambda表達式
         */
        @PostConstruct
        public void checkResultDispatcherInit() {
            checkResultDispatcher.put("校驗1", order -> String.format("對%s執行業務邏輯1", order));
            checkResultDispatcher.put("校驗2", order -> String.format("對%s執行業務邏輯2", order));
            checkResultDispatcher.put("校驗3", order -> String.format("對%s執行業務邏輯3", order));
            checkResultDispatcher.put("校驗4", order -> String.format("對%s執行業務邏輯4", order));
            checkResultDispatcher.put("校驗5", order -> String.format("對%s執行業務邏輯5", order));
            checkResultDispatcher.put("校驗6", order -> String.format("對%s執行業務邏輯6", order));
            checkResultDispatcher.put("校驗7", order -> String.format("對%s執行業務邏輯7", order));
            checkResultDispatcher.put("校驗8", order -> String.format("對%s執行業務邏輯8", order));
            checkResultDispatcher.put("校驗9", order -> String.format("對%s執行業務邏輯9", order));
        }
    
        public String getCheckResultSuper(String order) {
            //從邏輯分派Dispatcher中獲得業務邏輯代碼,result變量是一段lambda表達式
            Function<String, String> result = checkResultDispatcher.get(order);
            if (result != null) {
                //執行這段表達式獲得String類型的結果
                return result.apply(order);
            }
            return "不在處理的邏輯中返回業務錯誤";
        }
    }
    

    通過http調用一下看看效果:

    /**
     * 模擬一次http調用
     */
    @RestController
    public class BizController {
    
        @Autowired
        private BizService bizService;
    
        @PostMapping("/v1/biz/testSuper")
        public String test2(String order) {
            return bizService.getCheckResultSuper(order);
        }
    }
    

    這是個簡單的demo演示,之後會舉一些複雜的場景案例。

    魯迅曾說過,“每解決一個問題,就會引出更多的問題”。我們一起來看看這樣的實現有什麼好處,會帶來什麼問題。

    好處很直觀:

    1. 在一段代碼里直觀的看到”判斷條件”與業務邏輯的映射關係
    2. 不需要單獨定義接口與實現類,直接使用現有的函數式接口(什麼?不知道函數式接口?快去了解),而實現類直接就是業務代碼本身。

    不好的點:

    1. 需要團隊成員對lambda表達式有所了解(什麼?Java14都出來了還有沒用上Java8新特性的小夥伴?)

    接下來我舉幾個在業務中經常遇到的if else場景,並用Map+函數式接口的方式來解決它

    在真實業務場景問題的解決思路

    有的小夥伴會說,我的判斷條件有多個啊,而且很複雜,你之前舉個例子只有單個判斷邏輯,而我有多個判斷邏輯該怎麼辦呢?

    很好解決:寫一個判斷邏輯的方法,Map的key由方法計算出

    /**
     * 某個業務服務類
     */
    @Service
    public class BizService {
    
        private Map<String, Function<String, String>> checkResultDispatcherMuti = new HashMap<>();
    
        /**
         * 初始化 業務邏輯分派Map 其中value 存放的是 lambda表達式
         */
        @PostConstruct
        public void checkResultDispatcherMuitInit() {
            checkResultDispatcherMuti.put("key_訂單1", order -> String.format("對%s執行業務邏輯1", order));
            checkResultDispatcherMuti.put("key_訂單1_訂單2", order -> String.format("對%s執行業務邏輯2", order));
            checkResultDispatcherMuti.put("key_訂單1_訂單2_訂單3", order -> String.format("對%s執行業務邏輯3", order));
        }
    
        public String getCheckResultMuti(String order, int level) {
            //寫一段生成key的邏輯:
            String ley = getDispatcherKey(order, level);
    
            Function<String, String> result = checkResultDispatcherMuti.get(ley);
            if (result != null) {
                //執行這段表達式獲得String類型的結果
                return result.apply(order);
            }
            return "不在處理的邏輯中返回業務錯誤";
        }
    
        /**
         * 判斷條件方法
         */
        private String getDispatcherKey(String order, int level) {
            StringBuilder key = new StringBuilder("key");
            for (int i = 1; i <= level; i++) {
                key.append("_" + order + i);
            }
            return key.toString();
        }
    }
    

    用http模擬一下:

    /**
     * 模擬一次http調用
     */
    @RestController
    public class BizController {
    
        @Autowired
        private BizService bizService;
    
        @PostMapping("/v1/biz/testMuti")
        public String test1(String order, Integer level) {
            return bizService.getCheckResultMuti(order, level);
        }
    }
    

    只要設計好你的key的生成規則就好。

    還有小夥伴會問:我的業務邏輯有很多很多行,在checkResultDispatcherMuitInit()方法的Map中直接寫不會很長嗎?

    直接寫當然長了,我們可以抽象出一個service服務專門放業務邏輯,然後在定義中調用它就好了:

    提供一個業務邏輯單元:

    /**
     * 提供業務邏輯單元
     */
    @Service
    public class BizUnitService {
    
        public String bizOne(String order) {
            return order + "各種花式操作1";
        }
        public String bizTwo(String order) {
            return order + "各種花式操作2";
        }
        public String bizThree(String order) {
            return order + "各種花式操作3";
        }
    }
    
    /**
     * 某個業務服務類
     */
    @Service
    public class BizService {
        @Autowired
        private BizUnitService bizUnitService;
    
        private Map<String, Function<String, String>> checkResultDispatcherComX = new HashMap<>();
    
        /**
         * 初始化 業務邏輯分派Map 其中value 存放的是 lambda表達式
         */
        @PostConstruct
        public void checkResultDispatcherComXInit() {
            checkResultDispatcherComX.put("key_訂單1", order -> bizUnitService.bizOne(order));
            checkResultDispatcherComX.put("key_訂單1_訂單2", order -> bizUnitService.bizTwo(order));
            checkResultDispatcherComX.put("key_訂單1_訂單2_訂單3", order -> bizUnitService.bizThree(order));
        }
    
        public String getCheckResultComX(String order, int level) {
            //寫一段生成key的邏輯:
            String ley = getDispatcherComXKey(order, level);
    
            Function<String, String> result = checkResultDispatcherComX.get(ley);
            if (result != null) {
                //執行這段表達式獲得String類型的結果
                return result.apply(order);
            }
            return "不在處理的邏輯中返回業務錯誤";
        }
    
        /**
         * 判斷條件方法
         */
        private String getDispatcherComXKey(String order, int level) {
            StringBuilder key = new StringBuilder("key");
            for (int i = 1; i <= level; i++) {
                key.append("_" + order + i);
            }
            return key.toString();
        }
    }
    

    調用結果:

    總結

    最後,我們一起嘗試回答以下幾個問題:

    1. 策略模式是如何優化業務邏輯代碼結構的?
      —— 抽象了出了接口,將業務邏輯封裝成一個一個的實現類,任意地替換。在複雜場景(業務邏輯較多)時比直接 if else 來的好維護些。
    2. 殺雞焉用宰牛刀?就是幾個if else場景我需要用到策略模式?!——我們所不滿的其實就是傳統接口實現的缺點:1、策略類會很多。 2、業務邏輯分散到各個實現類中,而且沒有一個地方可以俯覽整個業務邏輯
    3. 有沒有什麼更好的代碼結構來實現策略模式的嗎?——針對傳統策略模式的缺點,分享了利用Map與函數式接口來實現的思路。

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

    【其他文章推薦】

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

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

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

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

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

  • F-立凱公布 8 月營收 續創新高

    9 月 1 日甫與日本電池技術領導者索尼簽署電動車鋰電池合作備忘錄的 F-立凱,公布 8 月單月合併營收為 9700 萬元,年增 92.4%,月增 16.4%。繼 7 月營收增長 64.7%,8 月營收續創歷史新高。受惠於兩岸挺進新能源車推動的政策方向確定,新能源車及儲能市場需求旺盛,推動電池正極材料銷售上揚。   立凱電是國內電動巴士系統與磷酸鐵鋰電池正極材料龍頭,該公司表示,下半年客戶需求強勁,產能滿載,看好未來車用電池與儲能電池及 4G 基地台之終端應用成長,立凱電材料歷年累積銷量已達 4700 噸。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

  • 算法崗面試題:模型的bias和variance是什麼?用隨機森林舉例

    算法崗面試題:模型的bias和variance是什麼?用隨機森林舉例

    校招在即,準備準備一些面試可能會用到的東西吧。希望這次面試不會被掛。

    基本概念

    說到機器學習模型的誤差,主要就是bias和variance。

    • Bias:如果一個模型的訓練錯誤大,然後驗證錯誤和訓練錯誤都很大,那麼這個模型就是高bias。可能是因為欠擬合,也可能是因為模型是弱分類器。

    • Variance:模型的訓練錯誤小,但是驗證錯誤遠大於訓練錯誤,那麼這個模型就是高Variance,或者說它是過擬合。

    這個圖中,左上角是低偏差低方差的,可以看到所有的預測值,都會落在靶心,完美模型;

    右上角是高偏差,可以看到,雖然整體數據預測的好像都在中心,但是波動很大。

    【高偏差vs高方差】
    在機器學習中,因為偏差和方差不能兼顧,所以我們一般會選擇高偏差、低方差的左下角的模型。穩定性是最重要的,寧可所有的樣本都80%正確率,也不要部分樣本100%、部分50%的正確率。個人感覺,穩定性是學習到東西的體現,高方差模型與隨機蒙的有什麼區別?

    隨機森林為例

    上面的可能有些抽象,這裏用RandomForest(RF)來作為例子:
    隨機森林是bagging的集成模型,這裏:
    \(RF(x)=\frac{1}{B}\sum^B_{i=1}{T_{i,z_i}(x)}\)

    • RF(x)表示隨機森林對樣本x的預測值;
    • B表示總共有B棵樹;
    • \(z_i\)表示第i棵樹所使用的訓練集,是使用bagging的方法,從所有訓練集中進行行採樣和列採樣得到的子數據集。

    這裏所有的\(z\),都是從所有數據集中隨機採樣的,所以可以理解為都是服從相同分佈的。所以不斷增加B的數量,增加隨機森林中樹的數量,是不會減小模型的偏差的。
    【個人感覺,是因為不管訓練再多的樹,其實就那麼多數據,怎麼訓練都不會減少,這一點比較好理解】

    【RF是如何降低偏差的?】
    直觀上,使用多棵樹和bagging,是可以增加模型的穩定性的。怎麼證明的?

    我們需要計算\(Var(T(x))\)
    假設不同樹的\(z_i\)之間的相關係數為\(\rho\),然後每棵樹的方差都是\(\sigma^2\).

    先複習一下兩個隨機變量相加的方差如何表示:
    \(Var(aX+bY)=a^2 Var(X)+b^2 Var(Y) + 2ab cov(X,Y)\)

    • Cov(X,Y)表示X和Y的協方差。協方差和相關係數不一樣哦,要除以X和Y的標準差:
      \(\rho=\frac{cov(X,Y)}{\sigma_X \sigma_Y}\)

    下面轉成B個相關變量的方差計算,是矩陣的形式:

    很好推導的,可以試一試。

    這樣可以看出來了,RF的樹的數量越多,RF方差的第二項會不斷減小,但是第一項不變。也就是說,第一項就是RF模型偏差的下極限了。

    【總結】

    • 增加決策樹的數量B,偏差不變;方差減小;
    • 增加決策樹深度,偏差減小;\(\rho\)減小,\(\sigma^2\)增加;
    • 增加bagging採樣比例,偏差減小;\(\rho\)增加,\(\sigma^2\)增加;

    【bagging vs boost】
    之前也提到過了boost算法:
    一文讀懂:GBDT梯度提升
    GBDT中,在某種情況下,是不斷訓練之前模型的殘差,來達到降低bias的效果。雖然也是集成模型,但是可以想到,每一個GBDT中的樹,所學習的數據的分佈都是不同的,這意味着在GBDT模型的方差會隨着決策樹的數量增多,不斷地增加。

    • bagging的目的:降低方差;
    • boost的目的:降低偏差

    喜歡的話請關注我們的微信公眾號~【你好世界煉丹師】。

    • 公眾號主要講統計學,數據科學,機器學習,深度學習,以及一些參加Kaggle競賽的經驗。
    • 公眾號內容建議作為課後的一些相關知識的補充,飯後甜點。
    • 此外,為了不過多打擾,公眾號每周推送一次,每次4~6篇精選文章。

    微信搜索公眾號:你好世界煉丹師。期待您的關注。

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

    【其他文章推薦】

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

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

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

    南投搬家公司費用需注意的眉眉角角,別等搬了再說!

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

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

  • F-貿聯營收隨 Tesla 出貨倍增 2015 年可望翻倍成長

    從 2005 年開始就與美國電動車大廠 Tesla 合作,而貿聯的營收比重也隨著 Tesla 成長,從 2013 年的 3~5%,到 2014 年倍增到 8% 至 10%,有機會晉升貿聯的前五大客戶。   據了解,2013 年同期貿聯打入 Tesla 的零件料號總數約 45 個,目前已倍增到 100 個,法人推估,平均貿聯交貨每台電動車用線的成本可達 300~400 美元,以 2015 年 Tesla 出貨量上看 10  萬輛來看,2015 年對貿聯的營收貢獻可望維持近倍成長,營收占比拉高至 15~20%。  

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

  • Java的前生今世

    Java的前生今世

    Java作為一門編程語言,自誕生以來已經流行了20多年,在學習它之前,我們有必要先了解一下它的歷史,了解它是如何一步步發展到今天這個樣子。

    孕育

    上世紀90年代,硬件領域出現了單片式計算機系統,比如電視機機頂盒這些消費設備。這些設備的特點是處理能力和內存都有限。如果使用C++編寫程序,則對於這類設備來說過於龐大。

    所以需求便來了,為了適應這類設備,需要一種小型的計算機語言,Java便應運而生了。

    1991年,Sun公司的James Gosling, Mike Sheridan和Patrick Naughton發起了一個名為“Green”的項目小組,主要用於電視機頂盒這類消費設備。

    由於設備的處理能力和內存有限,所以要求語言短小、代碼緊湊。而對於電視機頂盒這類消費設備來說,廠商會選擇各種不同的CPU,所以要求語言可移植、不與特定的平台捆綁在一起

    出生

    Green項目組以C++為基礎,對C++進行了一些改造,並刪除了一些不太實用、複雜繁瑣的東西,開發出了名為“Oak”的語言,(Oka來自James Gosling辦公室外的橡樹)。

    成長

    挫折

    1992年夏天,Green項目組已經能夠演示Green操作系統、Oak的程序設計語言、類庫和其硬件。

    1992年9月,Green項目組發布了它的第一個名為“Start 7”的產品,該產品是一個個人数字助理設備,具有圖形界面和非常智能的遠程控制。

    1992年11月,Green項目被剝離,成為Sun Microsystems的全資子公司Firstperson。

    但是當時的硬件生產廠商對其項目並不感興趣,Firstperson花了一年多也沒找到希望購買其技術的買家。

    1994年,First Person公司解散,被重新納入Sun公司。Oak語言無法進入市場,也被暫時擱置下來。

    機遇

    在上面的過程中,Internet的萬維網也在不斷髮展。對於客戶機/服務器模型來說,萬維網需要做的是將超文本頁面轉換到屏幕的瀏覽器中。

    1994年6月到7月,Sun公司的團隊看到了互聯網的發展,一些圖形Web瀏覽器的出現讓他們認為互聯網可以演變成他們為有線電視所設想的相同的高度交互性媒體。經過三天的頭腦風暴后,該團隊將目標平台重新定位為萬維網平台。

    1994年中期,Oak語言的開發者意識到他們可以開發一個瀏覽器,WebRunner瀏覽器誕生。

    1995年,互聯網的發展帶給了Oak語言一個機遇。當時的網絡頁面是單調、死板的靜態頁面,業界希望頁面能夠“靈活”起來,並且希望代碼能夠通過網絡傳播進行跨平台運行。Oak的語言短小、可移植的特點和這些需求不謀而合。

    Sun公司首先推出了可以嵌入網頁並且可以隨同網頁在網絡上傳輸的Applet(Applet是一種將小程序嵌入到網頁中進行執行的技術)。

    設計者在設計WebRunner瀏覽器具有在網頁中執行內嵌代碼的能力,這種能力能讓網頁變得“靈活”。

    同年,Sun公司將Oak更名為Java(因為Oak已經被使用了,Java一詞來自咖啡),WebRunner瀏覽器也演變成了HotJava瀏覽器,HotJava瀏覽器完全使用Java開發。

    同年5月23日,Sun公司在Sun World會議上展示並正式發布了Java語言和HotJava瀏覽器,各大公司都紛紛停止了自己公司相關項目的開發,競相購買Java的使用許可證,併為自己的產品開發了相應的Java平台。

    發展

    1996年初,Sun公司發布了Java的第1個開發工具包(JDK1.0),這是Java發展的里程碑,標志著Java成為了一種獨立的開發工具。

    同年9月,約8.3萬個網頁應用了Java來製作。

    同年10月,Sun公司發布了Java平台的第1個即時編譯器。

    但是人們很快意識到Java1.0並不能用來進行真正的應用開發。

    1997年2月,JDK1.1面世,它彌補了JDK1.0的大多明顯缺陷,改進了反射能力,但是仍有局限性。

    1998年12月,Sun公司發布了第二代Java平台——JDK1.2。此次發布有幾個不同的版本,分別是標準版J2SE、用於手機等嵌入式設備的微型版J2ME、用於服務器端處理的企業版J2EE。

    此次發布是Java發展過程中最重要的里程碑,標志著Java的應用開始普及。

    2000年5月,JDK1.3、JDK1.4、J2SE1.3相繼發布。

    2001年9月,J2EE1.3發布。

    2002年2月,J2SE1.4發布。該版本提高了Java的計算能力,擴展了標準類庫,提高了系統性能。

    在此期間,Java Applet採用低調姿態,淡化了客戶端的應用,成為服務器端應用的首選平台

    2004年9月,J2SE 1.5版本發布,該版本為重大更新,是自1.1版本以來第一個對Java語言做出重大改進的版本。JavaOne會議后,版本数字從1.5改為5.0。

    2005年6月,Java SE 6發布。版本名稱發生了改變,如J2SE改為Java SE、J2ME改為Java ME、J2EE改為Java EE。該版本沒有對語言方面再進行改進,但是改進了其他性能,增強了類庫。

    2006年11月,Sun公司將Java技術作為免費軟件對外發布。

    2007年3月起,全世界所有開發人員均可對Java源代碼進行修改。

    隨着數據中心越來越依賴於商業硬件而不是專用服務器,Sun公司在2009年被Oracle收購。

    2011年,Oracle發布Java SE 7。

    2014年,Java SE 8發布,近20年以來,該版本有了最大改變,Java 8提供了一種“函數式”編程方式,可以很容易地表述併發執行的計算。

    2017年,Java SE 9發布。

    Java 9之後,Oracle宣布每6月更新一次版本。

    2020年3月,Java SE 14發布。

    Java Web的發展

    Java一路走來,為了適應時代發展,做出了許多改變。

    在早期,Java是為了解決傳統的單機程序設計問題,但由於種種原因,Java後來轉向Web。

    Web的最初模樣

    Web為了提供交互性內容採取了客戶端/服務器系統,但是交互性完全由服務器提供。服務器產生靜態頁面,這些靜態頁面是事先寫好的,將其提供給能夠解釋並显示的客戶端瀏覽器。

    但是我們的需求是千變萬化的,事先寫好的靜態頁面肯定不能滿足我們日益增長的需求。

    CGI

    CGI(Common Gateway Interfa,通用網關接口),它是一段程序,運行在服務器上,提供同客戶端HTML頁面的接口,通俗的講CGI就像是一座橋,把網頁和WEB服務器中的執行程序連接起來,它把HTML接收的指令傳遞給服務器,再把服務器執行的結果返還給HTML頁。

    CGI使網頁變得不是靜態的,而是交互式的。

    但是CGI程序的響應時間依賴於發送的數據量的大小、服務器和網絡的負載,一旦網站變得複雜起來,響應就會變得非常慢且難以維護。

    Applet

    Applet是一種將小程序嵌入到網頁中進行執行的技術,含有Applet的網頁的HTML文件代碼中部帶有<applet></applet>這樣一對標記,當支持Java的網絡瀏覽器遇到這對標記時,就將下載相應的小應用程序代碼並在本地計算機上執行該Applet。

    在當時,Java Applet 可以大大提高Web頁面的交互能力和動態執行能力。

    Java剛出現時,Applet是最受大家歡欣鼓舞的。但是由於當時的網絡環境的問題,安裝Java運行時環境(JRE)所需要的10MB帶寬對當時的用戶來說太過於恐怖。並且微軟的IE瀏覽器並並沒有包含JRE,所以Applet並沒有得到大規模應用。

    Servlet

    Servlet(Server+Applet),即服務器端小程序。Servlet能夠按照用戶提交的內容處理並返回相應的資源。可以輸出HTML頁面,動態展示數據。Servlet實現正確的接口就能夠處理Http請求。

    但是出現了一個很大的問題:HTML代碼需要在Servlet類中用Java代碼一行一行輸出,表現、邏輯、控制、業務等全部在Servlet類中,造成Servlet類代碼混亂(HTML代碼、Java代碼都寫在一起),重複性高、閱讀性差、開發困難。

    下面是使用Servlet在瀏覽器中輸出有一個h1標題的Hello World!字符串的HTML頁面。

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter out = response.getWriter();
        response.setContentType("text/html;charset=utf-8");
        out.println("<html>");
        out.println("<head>");
        out.println("<title>$Title$</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h1>Hello World!</h1>");
        out.println("</body>");
        out.println("</html>");
    }
    

    可想而知,更複雜的頁面得寫多少代碼!

    JSP

    為了解決上面的問題,Sun公司開發出了JSP。

    JSP(JavaServer Page)是一種動態網頁技術標準。JSP部署於網絡服務器上,可以響應客戶端發送的請求,並根據請求內容動態地生成HTML、XML或其他格式文檔的Web網頁,然後返回給請求者。JSP技術以Java語言作為腳本語言,為用戶的HTTP請求提供服務,並能與服務器上的其它Java程序共同處理複雜的業務需求。

    可以將JSP頁面看成是一個能內嵌Java代碼的HTML頁面。

    靜態部分(HTML頁面)是模板,可以提前寫好,動態部分(數據)可以由Java代碼動態生成。這樣一來就避免了在單純使用Servlet時HTML代碼和Java代碼都出現在Servlet類中的混亂情況。

    JSP在運行時被轉換成Servlet,因此JSP本質是Servlet。

    下面是使用JSP做和上面同樣的事情:

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$</title>
      </head>
      <body>
        <h1>Hello World!</h1>
      </body>
    </html>
    

    仍使用HTML模板,但不是用Java語句一行一行“堆”出來,這多方便!

    使用了JSP后,雖然不用一行行地“堆”HTML頁面了,但是代碼混亂的問題仍沒有解決。表現、邏輯、控制、業務等代碼仍然寫在一塊了,只不過是從Servlet類中挪到JSP中了。

    這樣混亂的代碼極不利於團隊開發,如果項目很小,那還能理順,但如果項目非常大的話,那就“剪不斷,理還亂了”。

    所以這時候,我們急需將代碼分門別類的碼放整齊。

    JSP + JavaBean(Model1模式)

    JavaBean 是用Java語言寫成的可重用組件,JavaBean類要求:

    1. 類是公開的
    2. 具有私有成員變量
    3. 具有無參構造器
    4. 具有setter和getter方法

    上面已經說了,單純使用JSP會有大量的HTML代碼和Java代碼耦合在一起,不利於開發、維護、分工協作。

    我們舉一個登錄的例子:

    登錄大致需要以下幾步:

    1. 在瀏覽器中輸入用戶名、密碼
    2. 獲取數據,交給服務器
    3. 把數據處理成合適的形式
    4. 查詢數據庫的用戶表,看是否有一條用戶名和密碼都符合輸入的記錄。如果有,則登錄成功。

    單純使用JSP時,以上步驟全部在JSP登錄頁面中完成,該頁面既需要显示登錄表單、又需要獲取、處理數據、還需要查詢數據庫。太亂了!!

    有了JavaBean,我們可以將JSP中大量重複的代碼抽取成可重用的組件,封裝成JavaBean,在一定程度上減輕了代碼的耦合度,也在一定意義上實現了分層

    比如,用戶本身就是一個對象,我們可以將其抽取成JavaBean,數據的傳遞、查找都可使用JavaBean來完成。

    JSP+Servlet+JavaBean(Model2模式——MVC模式)

    上面介紹了Servlet、JSP、JSP+JavaBean,雖然在進步,但是都不盡如人意。那為什麼不讓它們結合起來取長補短?

    我們之所以使用JavaBean,就是為了讓Java代碼和HTML代碼分離開了,而Servlet也是一個Java類,為什麼不使用它呢?

    JSP+JavaBean已經實現了部分分層,使用MVC(Model-View-Controller)模式能進一步分層。

    Model(模型):MVC模式的中心組件,處理數據和邏輯。(比如JavaBean,但不止只有JavaBean)

    View(視圖):展示數據,渲染頁面。(前端頁面,比如HTML頁面、JSP頁面)

    Controller(控制器):從View接收數據,向Model發送數據,即用於控制數據分發。(比如Servlet)

    還用上面的登錄例子:

    1. 將JSP登錄頁面作為View

    2. Servlet類(Controller)用於接收用戶輸入的用戶名、密碼,將其封裝為用戶類(JavaBean),然後去數據庫中查詢。如果能查詢到則登錄成功。

    3. 將查詢結果返回給Controller,Controller根據查詢結果選擇一個合適的View,並將數據渲染到頁面,展示給用戶。(在本例中,如果查詢成功,則進入系統主頁;如果查詢不到,則用戶會看到一個登錄失敗頁面)

    使用了MVC設計模式后,代碼有了明顯的分層,結構清晰,各司其職。

    框架

    為了減輕工作量,各種框架逐漸出現在人們的視野中。

    所謂框架,可以把它看做一個半成品的項目/軟件,框架中有許多東西別人都已經幫我們寫好了,比如各種類、方法。

    我們要做的就是去了解、學習框架,在寫好的框架的基礎上、遵循框架的制定好的規則、按照自己的思路、結合框架的思想、去開發我們自己的項目。

    目前Java的Web開發中最出名的框架就是Spring家族了,比如SpringMVC、Spring、SpringBoot。

    熟悉、利用這些框架能夠幫助我們快速、優雅地開發出符合需求的項目。

    另,框架是別人寫好的輪子,有了它,我們能夠跑得更快,但是別忘了怎麼走。

    總結

    說了這麼多,相當於陪着Java從出生到現在走了一遍。我們可以看出Java的發展也不是一帆風順的,我認為Java之所以現在還這麼流行,原因之一就是它能夠一直保持更新,一直在根據時代的需求結合自身的特點不斷地做出改變。正如那句話「窮則變,變則通,通則達」。一門語言能做到這樣,身為人更應該要做到。

    如有錯誤,還請指正。

    參考資料:

    1. 百度百科
    2. 維基百科
    3. Java官方文檔
    4. Java核心卷一(第10版)
    5. Java編程思想(第4版)

    文章首發於公眾號『行人觀學』

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

    【其他文章推薦】

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

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

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

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

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