Home Blog Page 171

Tại sao phải chọn giữa R hay Python trong khi bạn có thể chọn cả 2?

Tại sao phải chọn ngôn ngữ lập trình R hay Python trong khi bạn có thể chọn cả 2?

Package reticulate cho phép ngôn ngữ lập trình R và Python có thể hoạt động cùng nhau – sau đây chính là 1 bài hướng dẫn mình muốn giới thiệu với các bạn.

Cả ngôn ngữ lập trình R và Python có nhiều điểm chung cũng như vài khác biệt. Hầu hết các ý tưởng tiềm ẩn về cấu trúc dữ liệu của 2 ngôn ngữ này khá là giống nhau. Có nhiều package data science đang tồn tại cho cả 2 ngôn ngữ lập trình này. Nhưng R lại được thiết lập theo 1 cách mà mình sẽ mô tả như là ‘dữ liệu trước, ứng dụng sau’. Trong khi Python thì  ngược lại khi nó nghiêng về việc phát triển ứng dụng được điều hướng từ ‘outlet’ nhiều hơn. Mình có 1 ví dụ: các lập trình viên Javascript sẽ dễ học ngôn ngữ lập trình Python hơn là R, vốn đã quen với syntax và việc quản lý ‘environment’. 

Hơn thế nữa, mình đã làm việc cả trên R và Python và đã có lúc gặp phải các tình huống mà mình sẽ muốn dùng cả 2 cùng với nhau. Điều này xảy ra vì 1 vài lý do, và lý do phổ biến nhất chính mình gặp phải là khi bạn đang build 1 thứ gì đó ở R, và bạn cần functionality mà chính bạn hay ai đó đã viết rồi bằng ngôn ngữ lập trình Python. Chắc chắn rằng bạn có thể viết lại nó với R, nhưng mình có cách tiện hơn để giúp bạn làm điều này.

Package reticulate trong R cho phép bạn có thể thực hành code Python bên trong 1 session của R. Thực ra package này đã có mặt được vài năm và ngày 1 phát triển hơn, nhưng phải đến gần đây mình mới thực sự cần tới nó. Nên sẵn tiện đây mình muốn viết vài dòng để tóm tắt 1 bài hướng dẫn luôn. Nếu bạn là 1 R native, việc sử dụng reticulate và chạy nó đòi hỏi bạn phải hiểu 1 chút về cách vận hành của Python – và cách nó thường quản lý ‘environment’ ra sao – và bài hướng dẫn của mình sẽ giúp bạn vận hành nó nhanh hơn khi là bạn phải tự mày mò 1 mình. 

Tuyển IT python nhiều ngành nghề hot

Environment trong R và Python

Bất kỳ dự án lập trình nào cũng đều tổ chức trong 1 environment, nơi chứa và truy cập tất cả mọi thứ nó cần hay được tạo nên trong quá trình vận hành của nó. Trong ngôn ngữ R, 1 environment tổng quát thông thường đều khả dụng với tất cả dự án ngôn ngữ dựa trên R và đều được cài đặt sẵn các gói cho việc truy cập. Có nghĩa là tất cả các dự án trong ngôn ngữ R thường đều được chạy trên cùng 1 environment cốt lõi (core environment) như nhau. Mình có 1 cách nghĩ về việc này là bạn hãy thử tưởng tượng tất cả mọi người trong nhà bạn đều sử dụng chung 1 cục sạc cho iPhone của họ. Nên để sạc điện thoại, họ phải rời khỏi phòng mình để tới nơi có sạc, và nếu họ bán chiếc điện thoại đi, người mua sẽ cần tự lo việc mua đồ sạc khác. 

Tuy nhiên, trong Python, mỗi dự án thường cần được thiết lập để hoàn toàn có thể ‘self-contain’ – với environment của riêng nó, bản copy base Python của riêng nó và các bản copy độc lập của tất cả ‘module’ mà nó cần để vận hành. Tương tự ví dụ trên bạn thử nghĩ về điều này như là mỗi người trong nhà bạn đều có riêng 1 cục sạc cho iPhone của họ. Họ không cần phải ra ngoài và cắm nó ở nơi nào khác, và khi họ bán chiếc điện thoại đi, thì nó cũng sẽ đi kèm với cục sạc rồi.

Trong khoản xét về các tiến trình cài đặt cũng như các nguồn tài nguyên disk/ memory thì model của Python khá là xa xỉ, nhưng bù lại lại nó cho phép việc chuyển đổi các cá thể độc lập đi kèm cấu hình tối thiểu của các dự án dễ dàng hơn. Không quá khó để thấy nó đã phát triển hiệu quả ra sao, khi nó vượt qua tư duy quy củ của việc phát triển phần mềm truyền thống, đó cũng là lý do tại sao mình nghĩ Python như hướng về ứng dụng (application driven) nhiều hơn. 

  Trải nghiệm lần đầu viết thư viện Python từ ngôn ngữ biên dịch

Đây là tí hình ảnh mình tự phác thảo ra để giải thích các khoản đơn giản về điểm khác biệt giữa cách mà các environment thường hoạt động trong ngôn ngữ R và Python: 

ngôn ngữ lập trình
ngôn ngữ lập trình

Cấu hình Environment điển hình trong R và Python

Giờ đây, nếu bạn muốn Python liên lạc được với R, thì nó vẫn cần tìm environment của riêng nó – bạn không thể chỉ đơn giản bắt nó truy cập global environment của R. Việc này cũng giống như bạn đang nói tiếng Việt với người nước ngoài để hỏi đường vậy..

Cho nên là để có thể làm Python hoạt động được bên trong dự án R của bạn, bạn cần phải có 2 thứ:

  1. 1 environment Python được thiết lập bên trong dự án R của bạn, để Python có thể định rõ phương hướng 
  2. Package reticulate cho việc chuyển ngữ code Python để nó có thể hoạt động được trong R

Thiết lập 1 environment cho Python

Từ giờ trở đi mình sẽ cho các bạn 1 ví dụ đơn giản. Cứ cho rằng mình đang có 1 dự án R trong RStudio, vốn đang cần 1 function mà mình đã viết rồi bằng ngôn ngữ lập trình Python. Và đây là function đơn giản mà mình đã lưu trong 1 script Python có tên light_years.py trong thư mục dự án R của mình có tên test_python (đúng vậy, RStudio cho phép bạn tạo các script Python!). Function này lấy khoản cách với đơn vị kilomet lẫn mile như là 1 input và tính toán sẽ mất bao nhiêu năm để di chuyển trong khoản cách đó với tốc độ ánh sáng – nói cách khác, khoản cách trong bao nhiêu năm ánh sáng sẽ là:

from scipy.constants import c
def light_years(dist, unit = "km"):
    c_per_year = c * 60 * 60 * 24 * 365.25
    if unit == "km":
        dist_meters = dist * 1000 
    elif unit == "mi":
        dist_meters = dist * 1.60934 * 1000
    else:
        sys.exit("Cannot use that unit!")
        
    return dist_meters/c_per_year

Mình đang dùng 1 function rất đơn giản ở đây để làm rõ cho bài viết này, hơi phi thực tế, hơi hơi ngớ ngẩn 1 chút vì mình đã import toàn bộ package scipy chỉ để lấy được value của hằng constant, hi vọng là bạn cũng hiểu được ý đồ của mình.  

Cũng như chúng ta đã bàn luận ở trên, chúng ta cần phải cung cấp cho đoạn code này 1 environment. Nó sẽ cần:

    1. 1 version của Python để hoạt động xuyên suốt
    2. Truy cập tới package scipy để nó có thể lấy constant c = tốc độ ánh sáng 

Không quá khó để thiết lập 1 environment Python cho dự án R của bạn. Cho thấy tầm quan trọng của environment dự án trong Python là như thế nào, sự hiện diện của nhiều công cụ quản lý environment, vốn cũng dễ dàng sử dụng. 

Bộ tool yêu thích của mình là Anaconda. Hiện đang có 2 version trên thị trường. Version đầy đủ, chứa cả vũ trụ rộng lớn bao gồm tất cả những gì mà 1 environment có thể cần, bao gồm tất cả những module thường được sử dụng nhất của Python. Và theo sau là Miniconda, vốn dễ dàng hơn trên disk space và thích hợp hơn cho nhiều người dùng Python được giới hạn. Bạn có thể lấy Miniconda cho hệ điều hành của bạn tại đây. Hãy chắc chắn rằng bạn đang tải Conda phù hợp với version Python bạn muốn làm việc cùng.

1 khi bạn đã cài đặt Conda rồi, nếu bạn đang dùng MacOS hay Linux, bạn sẽ thường phải cài đặt các environment của bạn bằng cách dùng dòng lệnh. Chỉ cần định vị nó tới thư mục dự án R (trong trường hợp của mình là test_python) trong phần terminal và dùng lệnh này:

conda create --name test_python

Chỉ đơn giản vậy thôi, bạn đã tạo ra được 1 environment Python. Mình thường đặt tên environment của mình giống như tên thư mục dự án để tránh sự nhầm lẫn trong tương lai.

Bây giờ bạn cần nói với Conda để dùng environment đó cho dự án này, cho nên hãy dùng lệnh này ngay tại dòng lệnh trong thư mục test_python của bạn:

conda activate test_python

Và bây giờ bạn đã liên kết dự án này tới environment Python, và có 1 bản sao của của base Python trong đó để đoạn code của bạn chạy qua.

Cuối cùng, function của chúng ta cần package scipy, nên chúng ta sẽ cần có nó bên trong environment. Điều này cũng dễ như việc chỉ cần gõ cái này vào bên trong thư mục dự án đã kích hoạt của bạn:

conda install scipy

Conda rồi sẽ cài đặt scipy và tất cả phụ thuộc mà nó nghĩ sẽ cần bên trong environment đang hoạt động của bạn và bạn đã sẵn sàng để bắt đầu rồi – dễ như ăn kẹo vậy.

Sau đó bạn sẽ cần nói với R nơi nó có thể tìm Python trong environment này, nên nếu bạn dùng lệnh sau đây, bạn có thể lấy 1 list tất cả các environment và hướng nó tới chỗ environment được cài đặt: 

conda info --envs

Điều này nói cho mình biết rằng, như 1 ví dụ, rằng environment của mình đã được cài đặt tại /Users/topdev/opt/miniconda3/envs/test_pythonMình vẫn luôn có thể tìm các executable Python bên trong thư mục ‘bin’ con – nên hướng đi đầy đủ tới executable Python cho dự án của mình là /Users/topdev/opt/miniconda3/envs/test_python/bin/python3, vì mình đang dùng Python 3. Đây là tất cả những gì chúng ta cần để nói với R nơi nó có thể tìm được environment của Python. 

  Viết chương trình Xoá các File trùng lặp bằng Python

Chạy function Python của bạn trong R


Bây giờ, dù bạn đã thiết lập environment Python như cách mình làm qua Conda, hay là bạn đã dùng virtualenv, bạn đã qua phần khó khăn nhất rồi. Các phần còn lại đơn giản hơn bởi vì đã có reticulate lo liệu.

Đầu tiên, bạn cần phải cho R biết nơi để tìm executable Python trong đúng environment nơi nó load dự án của bạn. Để làm điều này, hãy bắt đầu với 1 file text trống và thêm những điều sau đây, thay thế hướng đi của mình tới bất cứ hướng nào phù hợp với executable Python bên trong environment dự án bạn đã tạo.

Sys.setenv(RETICULATE_PYTHON = "/Users/topdev/opt/miniconda3/envs/test_python/bin/python3")

Bây giờ hãy lưu file text này bên trong thư mục dự án với tên .Renv. Đây là 1 file ẩn mà R sẽ thực thi bất cứ khi nào bạn mở project của bạn trong RStudio. Nên hãy shutdown RStudio và restart nó trong khi vẫn đang mở dự án test_python và nó bây giờ có thể được định vị tới environment của Python.

Nếu bạn vẫn chưa cài đặt package reticulate của R, thì đã đến lúc bạn cần phải làm rồi đó. 1 khi đã cài đặt, bạn có thể thử vài test trong phần terminal để xem nếu mọi thứ đang đi theo đúng hướng hay không. 

Đầu tiên bạn có thể test xem liệu R biết vị trí của Python không. reticulate::py_available() nên trả về “TRUE”. Bạn cũng có thể kiểm tra nếu các module Python bạn cần đã được cài đặt: reiculate::py_module_available(“scipy”) sẽ trả về “TRUE”. Giả sử cả 2 đều hoạt động tốt, bạn đã sẵn sàng để mang function của bạn vào R rồi.

Bạn có thể source đoạn script Python của bạn 1 cách đơn giản:

reticulate::source_python("light_years.py")

Giờ bạn đã có function light_years() sẵn sàng như 1 fucntion R. Hãy xem sẽ mất bao nhiêu năm để du hành 1 triệu tỷ dặm với tốc độ ánh sáng:

> light_years(1000000000000000, "mi")
[1] 170.1074

Thật tuyệt phải không nào! Rõ ràng đây là 1 ví dụ khá đơn giản nhưng nó thực sự cho bạn biết tất cả những thứ bạn cần về cách tích hợp code Python vào script R của bạn. Bạn có thể tưởng tượng cách bạn có thể đem tất cả các loại function hay package vốn chỉ đang dành riêng cho Python và làm cho chúng hoạt động được trong R – khá là thú vị phải không nào.

Gần đây mình cần một thuật toán phát hiện cộng đồng đồ thị mới (graph community detection algorithm) có tên là leidenalg cho sự triển khai vốn chỉ tồn tại trong Python, nhưng tất cả code dự án đang hiện hữu của mình lại đang ở R. Nên mình đã dùng reticuate như cách mình vẫn thường làm để giải quyết vấn đề này.

Để biết thêm về cách sử dụng Anaconda hay Miniconda để thiết lập environment Python, hướng dẫn sử dụng cho người dùng sẽ ở đây. Để tìm hiểu thêm về các loại function có sẵn để dịch từ Python sang R, có 1 họa tiết ‘reticulate khá ổn ở đây. Chào tạm biệt !

TopDev via towardsdatascience

Cơ hội việc làm cntt hấp dẫn tại TopDev đang chờ bạn!

Kambria Code Challenge – Cuộc thi mới dành cho các lập trình viên AI

Kambria Code Challenge là chuỗi các bài thi và hackathon trực tuyến liên quan đến lĩnh vực Trí tuệ nhân tạo (AI) được thiết kế để thu hút sự tham gia của các lập trình viên khắp nơi trên thế giới.

Đây là cơ hội để các bạn kiểm tra kiến thức về thuật toán AI, chứng minh các kỹ năng về lập trình của bạn, đồng thời nhận phần thưởng và phát triển cơ hội nghề nghiệp với các công ty công nghệ là đối tác của Kambria.

Quiz 02 của Kambria Code Challenge được tổ chức với chủ đề mới: Convolutional Neural Network. Cuộc thi sẽ diễn ra vào thứ bảy ngày 29 tháng 2 năm 2020. Các bạn hãy xem qua các nội dung trong bài viết bên dưới, click vào đường link và nhấp vào nút “Join This Challenge” để đăng ký tham gia.

Kambria Code Challenge – Quiz 02: http://bit.ly/KambriaQuiz02

Giải Thưởng Kambria Code Challenge:

  • Giải Nhất: trị giá $150
  • Giải Nhì: trị giá $100
  • 2 Giải Ba: mỗi giải trị giá $50

Giải thưởng sẽ được chi trả 50% bằng USDT, hoặc bằng Việt Nam Đồng, và 50% bằng KAT token.

Lưu ý: Quiz 04 sẽ là vòng thi cuối với giải thưởng lớn nhất của chuỗi cuộc thi Kambria Code Challenge. Các bạn thí sinh cần tham gia tối thiểu 2 quiz để được tham gia Quiz 04.

Ngoài ra, các lập trình viên có điểm số cao nhất khi tham dự các cuộc thi của Kambria Code Challenge sẽ có cơ hội được giới thiệu đến các công ty công nghệ là đối tác của Kambria cho cơ hội nghề nghiệp. 

Phần Thưởng Dành Cho Các Bạn Đăng Ký Sớm!

Chương trình sẽ tặng thưởng dành cho 150 bạn đăng ký sớm nhất tham gia Kambria Code Challenge. Các bạn cần hoàn thành bài quiz và đạt tối thiểu 25% tổng số điểm để được nhận giải.

Cấu trúc giải thưởng:

10,000 KAT token cho 20 bạn đăng ký sớm nhất

5,000 KAT token cho 30 bạn đăng ký tiếp theo

2,000 KAT token cho 100 bạn đăng ký còn lại

Các bước tham gia dự thi:

  1. Click “Join This Challenge”
  2. Điền đầy đủ thông tin trên trang hồ sơ cá nhân (Profile Page)
  3. Tham gia thi Kambria Code Challenge – Quiz 02

KAT là token được sử dụng trên platform của Kambria: nền tảng mã nguồn mở đầu tiên trên thế giới áp dụng công nghệ blockchain cho lĩnh vực AI và robotics.

Website: App.kambria.io/bounty

Tìm việc IT lương cao, đãi ngộ tốt trên TopDev ngay!

Tại sao team Discord chuyển từ Go sang Rust?

tai-sao-team-discord-chuyen-tu-go-sang-rust

Tác giả: Jesse Howarth

Ngôn ngữ Rust đang dần trở thành sự lựa chọn hàng đầu cho rất nhiều domain. Điển hình với Discord, chúng ta có thể thấy thành công của Rust trên cả client side và server side. Ví dụ, chúng tôi dùng nó cho đường pipeline mã hoá video từ Go Live (client side) và dùng cho Elixir NIFs (server side). Mới đây nhất, chúng tôi đã tiến hành cấp tốc cải thiện hiệu suất dịch vụ bằng cách chuyển từ Go sang Rust. Bài viết này sẽ giải thích thêm tại sao nó lại thích hợp với Discord chúng tôi, quy trình nó thế nào và kết quả hậu chuyển đổi ra sao.

The Read States service

Discord là một công ty thiên sản phẩm, nên hãy bắt đầu câu chuyện bằng một số chi tiết về product. Mảng dịch vụ mà chúng tôi thay Go bằng Rust được gọi là dịch vụ “Read States” – Theo dõi trạng thái đọc tin nhắn. Mục đích tối thượng của nó là theo dõi xem bạn đã đọc những tin nhắn và kênh nào. Cứ mỗi khi bạn mở Discord thì “Read States” sẽ được kích hoạt với mỗi khi tin nhắn được gửi đi hoặc được đọc.

Ngày trước khi ứng dụng Go, Read States không thể hỗ trợ một số yêu cầu về sản phẩm. Thường thì nó khá nhanh, nhưng cứ lâu lâu sẽ lại bắt gặp những khoảng chậm ảnh hưởng xấu đến trải nghiệm người dùng. Sau khi tìm hiểu chúng tôi đã hiểu ra các đợt nhiễu này là do feature chính của Go: Model bộ nhớ và Garbage Collector (GC) – bộ phận gom rác của nó.

Tại sao Go không đạt được kỳ vọng của chúng tôi?

Để giải thích cụ thể hơn tại sao Go không thể giúp chúng tôi đạt được thứ mình muốn, hãy cùng “đào mộ” sâu hơn về cấu trúc dữ liệu, quy mô, các access pattern, và kiến trúc của dịch vụ.

Cấu trúc data mà chúng ta hay dùng để lưu trữ thông tin trạng thái đọc thường gọi cho thuận là “Read State” (kiểu trạm Read State chứa read states…). Discord có cả tỉ cái Read State này. Cứ mỗi User một kênh là sẽ có một Read State. Mỗi Read State có rất nhiều counter cần được update hết và thường sẽ reset về lại 0. Ví dụ, một trong các counter sẽ là bạn có bao nhiêu lần @được_tag trên một kênh.

Để có được update counter nhanh gọn, mỗi Read State server đều có một cache Least Recently Used – LRU (cache ít được dùng nhất gần đây) trên Read States. Có đến hàng triệu users trên mỗi cache, và cả triệu Read States trên mỗi cache. Và chưa kể là cả trăm ngàn cái update cache mỗi giây. 

Cụ thể hơn, chúng tôi đã hỗ trợ cache bằng một cái database cluster Cassandra. Một khi cache key đã bỏ, chúng tôi sẽ gửi Read States của bạn về database. Chúng tôi còn set thời gian cho mỗi lần gửi về database mỗi 30 giây trong tương lai bất cứ khi nào Read State được cập nhật. Có khoảng cả mười ngàn database viết đè mỗi giây.

Trong ảnh bên dưới, bạn sẽ thấy thời gian phản hồi và cpu hệ thống trong thời gian đỉnh điểm khi còn dùng Go. Nếu bạn để ý thì cứ 2 phút sẽ có một đợt nhiễu CPU và chậm trễ khá mạnh.

Vậy tại sao lại là 2 phút?

Về việc dùng Go, mỗi khi bỏ cache key, bộ nhớ vẫn chưa trống ngay. Thay vào đó, bộ phận gom rác (garbage collector) sẽ chạy liên tục để tìm xem có phần bộ nhớ nào không có references và giải phóng nó. Nói cách khác, thay vì giải phóng phần bộ nhớ không còn dùng nữa, bộ nhớ sẽ còn ở đó một lát cho đến khi bộ phận gom rác xác định được có cần dùng đến nó nữa không. Trong thời gian này, Go sẽ phải làm rất nhiều thứ để xác định bộ nhớ nào làm chậm chương trình và cần giải phóng. 

Những đợt nhiễu chắc chắn đã biết được tác động của hiệu suất của bộ phận gom rác, tuy nhiên chúng tôi đã viết code Go rất hiệu quả rồi và có rất ít allocation (chỉ định). Chúng tôi không tạo quá nhiều rác.

Sau khi đào sâu hơn vào trong source code Go, chúng tôi nhận ra rằng Go sẽ buộc bộ phận gom rác này phải chạy mỗi 2 phút (tối thiểu). Nói cách khác, nếu bộ phận gom rác không hoạt động trong 2 phút, mặc cho có heap growth, Go vẫn bắt nó phải chạy.

Chúng tôi tìm ra rằng có thể điều chỉnh bộ phận gom rác để hoạt động thường xuyên hơn để ngăn chặn các trường hợp nhiễu lớn, vì thế chúng tôi ứng dụng một điểm endpoint lên dịch vụ để thay đổi ngay phần gom rác GC Percent. Đáng tiếc là dù cho chúng tôi định hình GC Percent như thế nào chẳng có gì thay đổi cả. Tại sao? Hoá ra lý do là vì chúng tôi chưa định vị bộ nhớ đủ nhanh để buộc garbage collector phải chạy thường xuyên hơn. 

Chúng tôi tiếp tục đào sâu hơn và hiểu ra rằng các đợt nhiễu lớn không phải do khối lượng lớn bộ nhớ cần giải phóng, mà là do garbage collector cần phải scan cả LRU cache để xác định xem bộ nhớ có hoàn toàn không được dùng đến nữa không. Ngay khi chúng tôi đã tìm được một cache LRU nhỏ hơn để nhanh hơn vì garbage collector chỉ sẽ scan ít hơn. Từ đó chúng tôi add thêm một setting mới vào service để thay đổi size của LRU cache và thay đổi cả kiến trúc để có thể chia ra nhiều cache LRU trên một server.

Và chúng tôi đã đúng! Với cache LRU nhỏ hơn, garbage collector gây ra các đoạn nhiều nhỏ hơn. 

Không may thay, cái phải đánh đổi cho việc dùng cache LRU nhỏ hơn đó là thời gian trễ cao hơn. Bởi vì nếu cache nhỏ hơn thì Read State của user có thể sẽ không nằm trên cache đó. Nếu vậy đồng nghĩa rằng sẽ phải lội vào database load …

Sau một lượng load đáng kể thử nhiều cache capacity khác nhau, chúng tôi tìm được một setting khá oke. Không thật sự xuất sắc, nhưng vừa-đủ-xài và với quy mô ngày càng lớn, chúng tôi để cho nó chạy như thế một thời gian.

Trong thời gian đó chúng tôi thấy được ngày càng nhiều công ty thành công với Rust ở những phần khác của Discord và cuối cùng đã quyết định sẽ tạo nên một framework và thư viện cần để build đầy đủ các dịch vụ trong Rust. Đây là ứng cử viên sáng giá để chuyển sang Rust vì nó nhỏ gọn, nhưng tôi cũng mong rằng Rust sẽ sửa được các lỗi nhiễu này. Vì thế chúng tôi tiến hành nhiệm vụ mới: chuyển Read State sang Rust, hi vọng rằng Rust đúng là một ngôn ngữ dịch vụ và cải thiện được trải nghiệm người dùng.

  Git - Học nghiêm túc một lần (Phần 1)
  Cấu hình SSH Key cho Github

Quản lý bộ nhớ trong Rust

Rust rất nhanh và thân thiện với bộ nhớ: không có runtime và garbage collector, nó có thể thúc đẩy các dịch vụ quan trọng về hiệu suất, chạy trên dịch vụ nhúng, và dễ tích hợp với các ngôn ngữ khác.

Rust không có bộ phận garbage collection, nên chúng ta sẽ tìm hiểu xem liệu nó có cùng nhiễu chậm như Go.

Rust sử dụng một phương pháp quản lý bộ nhớ rất độc mà kết hợp với dạng bộ nhớ “sở hữu”. Căn bản là, Rust sẽ theo dõi được ai có thể đọc và viết lên bộ nhớ. Nó hiểu rằng khi nào chương trình cần dùng bộ nhớ và giải phóng ngay lập tức khi nó không cần dùng đến nữa. Nó sẽ thúc đẩy quy luật của bộ nhớ trong thời gian compile, dường như không thể có bug runtime memory. Bạn sẽ không cần phải theo dõi bộ nhớ một cách thủ công – Compiler sẽ lo chuyện này.

Vậy trong phiên bản Rust của Read States services, khi Read State của một user đã bị xoá khỏi LRU cache nó sẽ lập tức được giải phóng khỏi bộ nhớ. Bộ nhớ read state không ở yên đợi garbage collector gom nó. Rust biết rằng nó không còn được dùng đến nữa và sẽ giải phóng nó ngay. Không có quá trình runtime nào để xác định nó có cần được giải phóng hay không.

Async Rust

Có một vấn đề với hệ sinh thái Rust. Vào thời gian này service đã được ứng dụng lại, Rust stable (**) không được thích hợp và thân thiện với Rust async cho lắm. Với dịch vụ network, lập trình bất đồng bộ (asynchronous programming) là một yêu cầu bắt buộc. Có rất ít các thư viện cộng đồng có thể kích hoạt được async Rust, nhưng nó sẽ đòi hỏi một lượng lớn thủ tục và các thông báo lỗi thì vô cùng khó hiểu.

May mắn thay, team Rust đã miệt mài ngày đêm để làm sao cho lập trình bất đồng bộ dễ dàng hơn, và nó đã có sẵn trên kênh của Rust. 

(**) Rust cung cấp 2 kênh distribution chính: nightly, beta, và stable. Các feature bất ổn định (unstable features) thì chỉ có trên nightly Rust.

Discord chưa bao giờ e dè trước công nghệ mới hứa hẹn cả. Ví dụ, chúng tôi là một trong những người đầu tiên áp dụng Elixir, React, React Native, và Scylla. Nếu có một phần công nghệ nào đó hứa hẹn, chúng tôi không ngại xử lý các khó khăn sẵn có và bất ổn định về edge. Đây là một trong những cách chúng tôi nhanh chóng đạt 250+ triệu user với chỉ dưới 50 kỹ sư.

Việc đón nhận các feature async mới trong Rust là một ví dụ điển hình cho thấy chúng tôi luôn sẵn lòng đón nhận các công nghệ mới và hứa hẹn. Là team kỹ thuật, chúng tôi quyết định chọn Rust nightly và sẽ tiếp tục chạy nightly cho đến khi async được hỗ trợ trên stable. Cứ thế chúng tôi đã cùng xử lý không biết bao nhiêu vấn đề, và đến hiện tại Rust stable đã hỗ trợ cho Rust async. Sự liều lĩnh của chúng tôi cuối cùng cũng xứng đáng.

Áp dụng, load testing và launch 

Việc viết lại code khá là nhanh gọn. Nó bắt đầu từ giai đoạn dịch, rồi gút nó lại sao cho có nghĩa. Ví dụ, Rust có hệ thống gõ rất tuyệt vời hỗ trợ rất tốt cho generic programming, nên chúng ta có thể vứt phần code Go trước đó tồn tại vì thiếu generics đi được rồi. Ngoài ra, Model bộ nhớ của Rust cũng có phần an toàn bộ nhớ trên các thread, nên chúng tôi mới có thể bỏ đi một số mục bảo vệ bộ nhớ thủ công trong Go.

Khi bắt đầu load testing, chúng tôi ngay lập tức hài lòng với kết quả. Độ trễ trong Rust thì tương đương với Go mà không có latency spikes nào!

Cái đáng nói là chúng tôi không phải tốn nhiều công sức vào việc tối ưu hoá vì bản Rust đã được viết sẵn rồi. Kể cả khi nó là tối ưu hoá căn bản, Rust cũng đã vượt trội hơn hẳn so với phiên bản cực kì thủ công của Go. Đây là một minh chứng tuyệt vời cho việc viết các chương trình hiệu quả với Rust dễ dàng như thế nào so với việc “đào bới” mà chúng tôi phải làm với Go.

Tuy nhiên, chúng tôi không hài lòng chỉ đơn giản vì nó giống với hiệu suất của Go. Sau một thời gian kiểm tra và tối ưu hoá hiệu suất, chúng tôi đã vượt xa Go trên gần như mọi thông số hiệu suất. Tất cả từ độ trễ, CPU, và bộ nhớ trên Rust đều tốt hơn.

Tối ưu hoá hiệu suất của Rust bao gồm: 

  1. Chuyển sang BTreeMap thay vì HashMap trên LRU cache để tối ưu hoá dung lượng bộ nhớ.
  2. Thay thế bớt thư viện thông số đầu tiên và thay bằng cái được dùng trong Rust concurrency mới.
  3.  Giảm số bản sao bộ nhớ đang làm. 

Từ đó mà chúng tôi chính thức vận hành như thế.

Việc launch thì khá trơn tru vì chúng tôi đã cho chạy thử. Chúng tôi đưa nó vào một canary node duy nhất, tìm thấy một vài trường hợp còn thiếu và sửa chúng. Ngay sau đó chúng tôi đã đưa nó ra toàn bộ dịch vụ.

Bên dưới là kết quả thu được.

Go là màu tím, Rust là màu xanh.

Nâng khả năng của cache 

Sau khi service đã chạy khá thành công được vài ngày, chúng tôi quyết định đã đến lúc thử nâng năng suất của LRU cache lên lần nữa. Trên bản Go như đã đề cập, việc nâng mức trần của LRU cache sẽ dẫn đến việc thu gom rác lâu hơn. Chúng tôi không phải giải quyết với phần gom rác nữa, nên chúng tôi phải tìm cách tăng size của cache và đạt hiệu suất tốt hơn. Chúng tôi tăng bộ nhớ trong các box, tối ưu hoá cấu trúc data để dùng ít bộ nhớ hơn, và tăng khả năng cache lên đến 8 triệu Read States. 

Chỉ cần nhìn các kết quả bên dưới bạn sẽ nắm rõ hơn rất nhiều. Bạn có thể thấy, thời gian trung bình đang được đo bằng micro giây và tối đa số @mentions được đo bằng mili giây.

Hệ sinh thái cải tiến 

Finally, another great thing about Rust is that it has a quickly evolving ecosystem. Recently, tokio (the async runtime we use) released version 0.2. We upgraded and it gave us CPU benefits for free. Below you can see the CPU is consistently lower starting around the 16th.

Cuối cùng, một điều tuyệt vời khác về Rust là nó có một hệ sinh thái phát triển nhanh chóng. Gần đây, tokio (thời gian chạy async mà chúng tôi sử dụng) đã phát hành phiên bản 0.2. Chúng tôi đã nâng cấp nó và nhận được CPU miễn phí. Dưới đây bạn có thể thấy CPU luôn thấp hơn bắt đầu từ ngày 16.

Lời kết

Đến thời điểm này, Discord đang sử dụng Rust cho nhiều mảng xuyên suốt stack phần mềm. Chúng tôi dùng nó cho game SDK, thu video và mã hoá cho Go Live, Elixir NIFs, các dịch vụ backend và nhiều thứ khác.

Khi bắt đầu project mới hay thành phần phần mềm, chúng tôi luôn cân nhắc về Rust trước. Dĩ nhiên là chúng tôi chỉ dùng nó khi thật sự phù hợp.

Ngoài hiệu suất ra, Rust cũng có nhiều ưu điểm cho team kĩ sư sử dụng. Ví dụ, phần type safety và borrow checker của nó rất dễ cho kỹ sư có thể code tái cấu trúc khi yêu cầu về sản phẩm thay đổi hoặc ngôn ngữ mới được phát hiện cần học. Ngoài ra, hệ sinh thái và công cụ rất tuyệt vời và có một động lượng đáng kể đằng sau chúng.

Nếu đã đọc đến đây, hy vọng rằng bạn đã có chút hứng thú với Rust hoặc đã thích Rust được một thời gian. Nếu bạn muốn được giải quyết các vấn đề liên quan đến Rust một cách chuyên nghiệp, đừng ngần ngại cân nhắc apply vào Discord. 

Đừng bỏ lỡ những bài viết hay liên quan:

Xem thêm việc làm Software Developers hấp dẫn tại TopDev

Bài viết gốc được đăng tải tại Blog.Discorsapp

7 lý do bạn không nên sử dụng TypeScript

7-ly-do-ban-khong-nen-su-dung-typescript

TypeScript có phải là con ác chủ bài, ngôn ngữ của tương lai, viết TypeScript thì mọi thứ sẽ không thể nào còn lỗi, những lý do khiến bạn phải suy nghĩ lại những nhận định trên

Rất nhiều người yêu TypeScript, nó “giải quyết” rất nhiều vấn đề mà JS gặp phải, một ngôn ngữ “thay thế” cho JS, nó sẽ báo bạn ngay nếu code bạn có vấn đề và dễ đọc hơn. Có nhiều nhiều lý do được đưa ra để bạn nên dùng TypeScript, bài này ngược lại đưa cho bạn 7 lý do không nên dùng TypeScript

Tìm việc làm Typescript lương cao lên đến 3000 USD

Quá mạo hiểm

Tại sao lại mạo hiểm? Nếu TypeScript thêm định nghĩa type và kiểm tra lúc compile, IDE còn thông báo ngay lúc nếu kiểu dữ liệu không khớp. Chính xác đó là lý do. TypeScript chỉ kiểm tra kiểu dữ liệu lúc compile và chỉ sử dụng kiểu có định nghĩa. Tất cả các network call, API và thư viện bổ sung chưa có type sẽ không có cách nào tương tác với TypeScript.

Nếu trong JS, chúng ta không đặt giả định về kiểu sẽ nhận được, không tự nhủ “cái này chắc chắn” sẽ trả về kiểu string, chúng ta luôn biết phải kiểm tra giá trị thật sự của biến nhận được trước khi sử dụng. Với TS, bạn phụ thuộc compiler làm việc này, nhưng sẽ có rất nhiều thứ phải làm. Bạn vừa phải bỏ thời gian viết định nghĩa cho từng tỉ tỉ thứ, rồi bỏ thêm mớ thời gian để đảm bảo các định nghĩa bạn viết ra phải đúng lúc chạy, vậy mục tiêu cuối cùng của tất cả những thứ đó là gì?

Quá rối rắm

Mặt trái của sự thật: một ngôn ngữ được kỳ vọng sẽ đem đến sự minh bạch, sạch sẽ, dễ đọc hơn lại đem đến điều người lại. Để minh họa, hãy nhìn thử một đoạn TS sau

// TODO: do this more elegantly
;((currentReducer as unknown) as Reducer<NewState,NewActions>) = nextReducer

static create: Function = <T>(subscribe?: (subscriber: Subscriber<T>) => TeardownLogic) => {
  return new Observable<T>(subscribe);
}

 

Đóng code trên lấy từ thư viện Redux và RxJS. Nếu bạn đang viết React và thích HOC, thì bạn sẽ hiểu viết bổ sung TypeScript sẽ đem đến mồ hôi và nước mắt cho các đồng nghiệp khác như thế nào.

Không giải quyết vấn đề

TS bảo là giải quyết các vấn đề mà JS đang gặp. Nhưng sự thật là KHÔNG. Dynamic typing chưa bao giờ là vấn đề với những lập trình viên JS (có mình luôn), bạn sẽ phàn nàn vậy “NaN === NaN” không phải là vấn đề sau, việc có cũng được không có cũng được dấu chấm phẩy ; không phải là vấn đề à,… một vài lý do khác nữa. TypeScript cũng chẳng giải quyết như bạn tưởng tượng đâu, nó chỉ giới thiệu một chuẩn mới, làm phân cực công đồng JS thêm thôi.

Thậm chí, nếu việc thiếu type trong JS là một vấn đề, TS không giải quyết luôn. Những ngôn ngữ thật sự giải quyết nó là Java, C, C# và các ngôn ngữ compiled.

Chẳng thay thế, chỉ là phần mở rộng

TS sau cùng cũng complie về JS, nó không hề là một ngôn ngữ có thể thay thế JS như tự sướng. Những gì TypeScript có thể làm, sẽ bị giới hạn trong những gì JS làm được. Đừng ảo tưởng với TS là bạn đã đủ chinh chiến trên mọi chiến trường, đừng tin vào lời dối trá đó, hãy tìm hiểu nhiều hơn sức mạnh thực sự của JS và linh động kiểu dữ liệu mang lại gì cho bạn, lúc đó bạn sẽ thấy mình đã tiến xa hơn những gì cái khung TS đã đóng bạn lại.

Mã nguồn mở, có thật vậy không?

Nhiều lý do đưa ra khi sử dụng TS là vì nó mã nguồn mở. Đúng, nhưng chưa đủ. Nó vẫn chịu sự chi phối từ Microsoft, một tập đoàn độc quyền khổng lồ nổi tiếng nhất thế giới, Microsoft chia sẻ mã nguồn nó như một động thái tiếp thị và lôi kéo thêm lập trình viên. Đừng lẫn lộn giữa mã nguồn mở với sự dân chủ: Microsoft vẫn ở đây và có quyền làm mọi thứ với TS, bạn chẳng làm gì được ngoài việc đứng nhìn. JS, lại khác, được cộng đồng đảm trách, sẽ không thay đổi bất cứ thứ gì nếu không được sự đồng ý từ số đông cộng đồng.

Các công ty lớn sử dụng…

Không ít các cá nhân có trách nhiệm lựa chọn ngôn ngữ cho dự án lại đưa đây làm một lý do để sử dụng TS. Vậy có tính đến chuyện các công ty lớn cũng có những bộ codebase cũ mèm, lỗi thời. Việc người khác chọn một thứ gì đó, chắc gì thứ đó cũng hợp với mình.

Nhiều tính năng hơn

Xưa rồi, khi TS được giới thiệu năm 2012, các tính năng như class chưa có trên JS. Nhưng nay đã là 2020, hơn 8 năm nay, JS đã tiến một bước rất xa, phải nói đúng hơn là giờ TS còn phải chạy theo JS

Từ quan điểm của một cá nhân không thấy nhiều lợi ích mà TypeScript mang lại.

Kết quả hình ảnh cho TypeScript

Bài viết gốc được đăng tải tại Vuilaptrinh

Tham khảo thêm các vị trí tuyển lập trình it hấp dẫn tại đây

Tìm hiểu tham số và đối số trong hàm C++

Đặt vấn đề

Trong bài học trước, chúng ta đã học về hàm có thể trả về một giá trị cho người gọi hàm đó. Chúng ta đã sử dụng điều đó để tạo ra một hàm getValueFromUser mà chúng ta đã sử dụng trong chương trình này:

#include <iostream>
int getValueFromUser()
{
 	std::cout << "Enter an integer: ";
	int input{};
	std::cin >> input;  
	return input;
}
 
int main()
{
	int num { getValueFromUser() };
	std::cout << num << " doubled is: " << num * 2 << '\n';
	return 0;
}

Tuy nhiên, điều gì sẽ xảy ra nếu chúng ta muốn đầu ra của một hàm để cho vào một hàm khác? Bạn có thể thử một cái gì đó như thế này:

#include <iostream>
 
int getValueFromUser() // this function now returns an integer value
{
 	std::cout << "Enter an integer: ";
	int input{};
	std::cin >> input;  
	return input; // added return statement to return input back to the caller
}
 
// This function won't compile
// The void return type means the function won't return a value to the caller
void printDouble()
{
	std::cout << num << " doubled is: " << num * 2 << '\n';
}
 
int main()
{
	int num { getValueFromUser() };
	printDouble();
	return 0;
}

Điều này đã không được biên dịch, vì hàm printDouble không biết định danh num là gì?. Bạn có thể thử định nghĩa num là một biến bên trong hàm printDouble():

void printDouble()
{
	int num{}; // we added this line
	std::cout << num << " doubled is: " << num * 2 << '\n';
}

Mặc dù điều này giải quyết lỗi của trình biên dịch và làm cho chương trình có thể biên dịch được, nhưng chương trình vẫn không hoạt động chính xác (nó luôn in ra 0 và 0 nhân đôi là 0). Cốt lõi của vấn đề ở đây là hàm printDouble không có cách truy cập giá trị mà người dùng đã nhập.

Chúng ta cần một số cách để truyền giá trị của num cho hàm printDouble để printDouble có thể sử dụng giá trị đó trong thân hàm của nó.

Tìm việc làm C++ nhanh chóng trên TopDev

Tham số và đối số

Trong nhiều trường hợp, Khi có thể truyền thông tin đến một hàm được gọi, để hàm đó có dữ liệu để làm việc. Ví dụ, nếu chúng ta muốn viết một hàm để thêm hai số, chúng ta cần một số cách để nói cho hàm biết về hai số sẽ thêm khi chúng ta gọi nó. Chúng tôi làm điều đó thông qua các tham số của hàm và đối số.

Tham số và đối số

Tham số là gì?

Tham số (Parameter) là một biến được sử dụng trong một hàm. Các tham số của hàm hoạt động gần như giống hệt với các biến được định nghĩa bên trong hàm, nhưng có một điểm khác biệt là chúng luôn được khởi tạo với một giá trị được cung cấp bởi người gọi của hàm.

Ví dụ trong C++:

void greet(std::string name) {
    std::cout << "Hello, " << name << "!" << std::endl;
}

Trong ví dụ trên, name là một tham số của hàm greet. Nó đại diện cho giá trị mà hàm sẽ nhận khi được gọi.

Tham số xác định loại dữ liệu mà hàm sẽ xử lý và cách thức mà hàm hoạt động dựa trên dữ liệu đó.

Các tham số của hàm được định nghĩa trong khai báo hàm bằng cách đặt chúng ở giữa dấu ngoặc đơn sau tên hàm, với nhiều tham số được phân tách bằng dấu phẩy.

Dưới đây là một số ví dụ về các hàm với số lượng tham số khác nhau:

// This function takes no parameters
// It does not rely on the caller for anything
void doPrint()
{
    std::cout << "In doPrint()\n";
}
 
// This function takes one integer parameter named x
// The caller will supply the value of x
void printValue(int x)
{
    std::cout << x  << '\n';
}
 
// This function has two integer parameters, one named x, and one named y
// The caller will supply the value of both x and y
int add(int x, int y)
{
    return x + y;
}

Đối số là gì?

Đối số (Argument) là một giá trị được truyền từ người gọi đến hàm khi thực hiện lệnh gọi hàm. Đối số được truyền cho các tham số tương ứng đã được định nghĩa trong hàm, và nó xác định giá trị cụ thể mà hàm sẽ xử lý.

Ví dụ

greet("Alice");

Trong ví dụ này, "Alice" là một đối số được truyền vào hàm greet. Đối số này sẽ được gán cho tham số name, và hàm greet sẽ thực hiện in ra dòng chữ “Hello, Alice!”.

Một ví dụ khác:

doPrint(); // this call has no arguments 
printValue(6); // 6 is the argument passed to function printValue() 
add(2, 3); // 2 and 3 are the arguments passed to function add()

Lưu ý rằng nhiều đối số được phân tách bằng dấu phẩy.

Cảnh báo về thứ tự đối số của hàm

Đặc tả trong C ++ không định nghĩa rằng các đối số có khớp với các tham số theo thứ tự từ trái sang phải hoặc từ phải sang trái hay không. Khi sao chép giá trị, thứ tự là không thống nhất. Tuy nhiên, nếu các đối số là các lệnh gọi hàm, thì điều này có thể có vấn đề:

someFunction(a(), b()); // a() or b() may be called first

Nếu xem xét từ trái sang phải, a() sẽ được gọi trước b(). Nếu xem xét từ phải sang trái, b() sẽ được gọi trước a(). Điều này có thể có hoặc không có hậu quả, tùy thuộc vào những gì hàm a() và b() làm.

Nếu điều quan trọng là một đối số đ trước, bạn nên xác định rõ thứ tự thực hiện, như vậy:

int avar{ a() }; 

// a() will always be called first 

int bvar{ b() }; 

// b() will always be called second 

someFunction(avar, bvar); 

// it doesn't matter whether avar or bvar are copied first because they are just values

Đặc tả trong C ++ không xác định liệu các hàm sẽ lấy các đối số từ trái sang phải hay phải sang trái. Do đó nên cẩn thận không thực hiện các cuộc gọi hàm ngay trong các đối số của hàm nào đó khi có vấn đề trong thứ tự gọi các hàm.

Làm thế nào các tham số và đối số làm việc với nhau

Khi một hàm được gọi, tất cả các tham số của hàm được tạo dưới dạng các biến và giá trị của từng đối số được sao chép một cách khớp vào tham số tương ứng. Quá trình này được gọi là gán giá trị vào (pass by value).

Ví dụ:

#include <iostream>
 
// This function has two integer parameters, one named x, and one named y
// The values of x and y are passed in by the caller
void printValues(int x, int y)
{
    std::cout << x << '\n';
    std::cout << y << '\n';
}
 
int main()
{
    printValues(6, 7); // This function call has two arguments, 6 and 7
 
    return 0;
}

Khi hàm printValues ​​được gọi với các đối số 6 và 7, tham số x của printValues được tạo và khởi tạo với giá trị 6 và tham số y của printValues được tạo và khởi tạo với giá trị 7.

Điều này dẫn đến kết quả đầu ra:

6 7

Một ví dụ khác, cộng hai số lại với nhau và trả về kết quả cho người gọi:

#include <iostream>
 
// add() takes two integers as parameters, and returns the result of their sum
// The values of x and y are determined by the function that calls add()
int add(int x, int y)
{
    return x + y;
}
 
// main takes no parameters
int main()
{
    std::cout << add(4, 5) << '\n'; // Arguments 4 and 5 are passed to function add()
    return 0;
}

Thực hiện bắt đầu chạy từ đầu hàm main. Khi add (4, 5) được thực hiện, hàm add được gọi, tham số x được khởi tạo với giá trị 4 và tham số y được khởi tạo với giá trị 5.

Câu lệnh return trong hàm add sẽ thực hiện x + y để tạo ra giá trị 9, sau đó được trả về hàm main. Giá trị 9 này sau đó được gửi đến std :: cout để được in lên console.

Đầu ra: 9

Lưu ý rằng số lượng đối số thường phải khớp với số lượng tham số hàm hoặc không thì trình biên dịch sẽ đưa ra lỗi. Đối số được truyền cho một hàm có thể là bất kỳ biểu thức hợp lệ nào (vì về cơ bản, đối số chỉ là một trình khởi tạo giá trị cho tham số và các khởi tạo có thể là bất kỳ biểu thức hợp lệ nào).

Tuyển dụng intern C++ đãi ngộ tốt, ứng tuyển ngay TopDev

So sánh tham số và đối số

Mặc dù tham số và đối số liên quan mật thiết với nhau, nhưng có những khác biệt quan trọng mà bạn cần hiểu rõ:

Tiêu Chí Tham Số (Parameter) Đối Số (Argument)
Định Nghĩa Biến được khai báo trong định nghĩa của hàm, đại diện cho giá trị mà hàm sẽ nhận khi được gọi. Giá trị thực tế được truyền vào hàm khi gọi hàm.
Vị Trí Được khai báo trong phần định nghĩa của hàm. Được truyền vào trong phần gọi hàm.
Chức Năng Xác định loại dữ liệu và số lượng dữ liệu mà hàm có thể nhận. Cung cấp giá trị cụ thể mà hàm sẽ xử lý trong một lần gọi cụ thể.
Ví Dụ int sum(int a, int b)ab là tham số. sum(3, 7)37 là đối số.
Phạm Vi Hoạt động như biến cục bộ bên trong hàm. Giá trị cụ thể được gán cho tham số khi hàm được gọi.
Tính Mặc Định Có thể có giá trị mặc định trong một số ngôn ngữ lập trình. Không có giá trị mặc định; giá trị được xác định khi gọi hàm.
Thời Điểm Được Xác Định Xác định tại thời điểm định nghĩa hàm. Xác định tại thời điểm gọi hàm.

Bảng này tóm tắt sự khác biệt chính giữa tham sốđối số, giúp dễ dàng phân biệt và áp dụng đúng trong lập trình.

Trên đây là tổng hợp kiến thức về tham số và đối số trong C++, đăng ký theo dõi TopDev để tiếp tục series tự học C++ bạn nhé!

Nguồn tham khảo: CafeDev
Xem ngay những tin đăng tuyển dụng IT mới nhất trên TopDev

[Tự học C++] Giới thiệu literals và operators

Literals

Hãy xem xét hai dòng sau:

1
2
std::cout << "Hello world!";
int x{ 5 };

“Hello word” là gì?. Nó là literal, Một chữ(literal) (còn được gọi là hằng số kiểu chuỗi(literal constant)) là một giá trị cố định đã được chèn trực tiếp vào code.

Tuy nhiên, giá trị của một chữ(Literals) là cố định và không thể thay đổi (do đó nó được gọi là hằng số), trong khi giá trị của một biến có thể được thay đổi thông qua khởi tạo và gán.

Operators(Toán tử)

Trong toán học, một phép toán là một phép tính toán liên quan đến 0 hoặc nhiều giá trị đầu vào (được gọi là toán hạng) tạo ra một giá trị mới (được gọi là giá trị đầu ra). Một hoạt động cụ thể được thực hiện bằng một cấu trúc nào đó (thường là ký hiệu hoặc cặp ký hiệu) được gọi là toán tử.

Ví dụ, như trẻ em, tất cả chúng ta đều học rằng 2 + 3 bằng 5. Trong trường hợp này, 2 và 3 là các toán hạng và ký hiệu + là toán tử cho chúng ta áp dụng phép toán cộng trên toán hạng để tạo ra giá trị mới 5.

Ví dụ: toán tử cộng sẽ được gọi là toán tử + và toán tử trích xuất sẽ được gọi là toán tử >>.

Bạn có thể đã khá quen thuộc với các toán tử số học từ việc sử dụng khá phổ biến trong toán học, bao gồm phép cộng (+), phép trừ (-), phép nhân (*) và phép chia (/). Trong C ++, gán (=) cũng là một toán tử, cũng như << (chèn) và >> (trích xuất). Một số toán tử được sử dụng nhiều hơn một ký hiệu, chẳng hạn như toán tử đẳng thức (==), cho phép chúng ta so sánh hai giá trị để xem chúng có bằng nhau không. Ngoài ra còn có một số toán tử là các từ (ví dụ: new, delete và thrown).

Các toán tử trong C ++ có ba loại khác nhau:

Toán tử đơn(Unary operators) chỉ hành động trên một toán hạng. Một ví dụ về toán tử đơn là toán tử -. Ví dụ, đã cho -5, toán tử – lấy toán hạng bằng 5 và lật dấu của nó để tạo ra giá trị đầu ra mới -5.

Toán tử nhị phân(Binary operators) hoạt động trên hai toán hạng (được gọi là trái và phải). Một ví dụ về toán tử nhị phân là toán tử +. Ví dụ, được cho 3 + 4, toán tử + lấy toán hạng bên trái (3) và toán hạng bên phải (4) và áp dụng phép toán bổ sung để tạo ra giá trị đầu ra mới là 7. Toán tử chèn (<<) và trích xuất (>>) là toán tử nhị phân , lấy std :: cout hoặc std :: cin ở bên trái để xuất hoặc biến thành đầu vào ở bên phải.

Toán tử ternary(Ternary operators) hành động trên ba toán hạng. Cái này chúng tôi sẽ trình bày sau.

Lưu ý rằng một số toán tử có nhiều hơn một nghĩa tùy thuộc vào cách chúng được sử dụng. Ví dụ, toán tử – có hai bối cảnh. Nó có thể được sử dụng ở dạng đơn nhất để đảo ngược một số ký hiệu (ví dụ: để chuyển đổi 5 thành -5 hoặc ngược lại) hoặc có thể được sử dụng ở dạng nhị phân để thực hiện phép trừ (ví dụ: 4 – 3).

Tìm việc làm C++ nhanh chóng trên TopDev

Chuỗi operators

Các toán tử(operators) có thể được nối với nhau sao cho đầu ra của một toán tử có thể được sử dụng làm đầu vào cho một toán tử khác. Ví dụ, được đưa ra như sau: 2 * 3 + 4, toán tử nhân đi trước và chuyển đổi toán hạng trái là 2 và toán hạng phải là 3 thành giá trị mới là 6 (trở thành toán hạng bên trái cho toán tử cộng). Tiếp theo, toán tử cộng thực thi và chuyển đổi toán hạng trái là 6 và toán hạng phải là 4 thành giá trị mới là 10.

Chúng tôi sẽ nói nhiều hơn về thứ tự cuả các toán tử về sau này. Cho đến nay, nó đủ để biết rằng các toán tử số học thực hiện theo thứ tự giống như chúng làm trong toán học tiêu chuẩn: Dấu ngoặc đơn trước, sau đó là Số mũ, sau đó là Phép nhân & Phép chia, rồi Phép cộng & Phép trừ.

Nguồn gốc bài viết từ CafeDev
Xem ngay những tin đăng tuyển dụng IT mới nhất trên TopDev

5 nguyên nhân chính vì sao các nhà khoa học dữ liệu rời bỏ công việc của mình

5 nguyên nhân chính vì sao các nhà khoa học dữ liệu rời bỏ công việc của mình

Giới thiệu

Hiện nay, biểu đồ của nhà khoa học dữ liệu đang ở mức đỉnh điểm. Hướng tới năm 2020, thực sự không có quá nhiều các chuyên gia ở ngoài kia có thể phân biệt trắng, đen hay tuân thủ các câu lệnh từ 1 nhà khoa học dữ liệu.

Mình đã từng thấy mấy ông ‘không phải là khoa học gia dữ liệu’ (hay các gã ‘không phải là kĩ thuật viên’) nhìn vào 1 nhà khoa hc dữ liệu như là 1 siêu nhân. Có hàng tá các lý do cho việc này (gồm cả việc cường điệu quá theo truyền thông) nhưng không cần phải bận tâm vì công việc của 1 nhà khoa học dữ liệu là 1 việc làm cao quý.

Hãy xem thử biểu đồ ‘Hype Cycle’ được xuất bản bởi Gartner cho Artificial Intelligence năm 2019 bên dưới: 

5 nguyên nhân chính vì sao các nhà khoa học dữ liệu rời bỏ công việc của mình

Để ủng hộ, đây là “Báo cáo về các việc làm triển vọng của Linkedin” và mình chắc rằng bạn sẽ đoán ra ngay công việc đang đứng top danh sách là gì rồi:

Các con số này khá là ngạc nhiên. Từ các công ty ‘Fortune 500’ tới các của hàng bán lẻ, tổ chức vòng quanh thế giới đều muốn xây dựng 1 đội hình bao gồm các chuyên gia khoa học dữ liệu hàng đầu. Năm 2019 vốn đã phá mọi kỉ lục trước đó của việc đầu tư vào ngành khoa học dữ liệu và AI.

Nhưng mặc dù có bao nhiêu xu hướng tích cực này, mình vẫn có 1 cảm giác không thoải mái tiềm ẩn. Các nhà khoa học dữ liệu đang từ bỏ hay đổi nghề 1 cách nhanh chóng. Tại sao điều này lại đang xảy ra? Liệu có điều gì mà chúng ta chưa biết?

Hãy phân tích 5 lý do chính tại sao các nhà khoa học dữ liệu lại đang rời bỏ công việc gần như là ước mơ của họ. Nếu như bạn cũng đang đối mặt với điều này, hay muốn chia sẻ kinh nghiệm riêng của mình, hãy chia sẻ nó với cộng đồng trong phần bình luận cuối bài nhé !
5 nguyên nhân chính vì sao các nhà khoa học dữ liệu rời bỏ công việc của mình

  1. Kỳ vọng và Thực tế – Góc khuất to lớn !

Đây là 1 trong những vấn đề phổ biến nhất trong lĩnh vực khoa học dữ liệu. Có 1 khuất mắt ngày càng lớn giữa việc các nhà khoa học dữ liệu mong đợi và việc họ thực sự làm trong ngành công nghiệp này.

Có nhiều nguyên nhân cho điều này và có thể đa dạng tùy theo trường hợp của mỗi người. Các cấp độ về kinh nghiệm cũng đóng vai trò lớn trong vực sâu của sự kỳ vọng này.
5 nguyên nhân chính vì sao các nhà khoa học dữ liệu rời bỏ công việc của mình

Hãy lấy ví dụ về các nhà khoa học dữ liệu đầy tham vọng. Thông thường, họ tự học và thu thập kiến thức thông qua sách vở và các khóa học online. Họ không có cơ hội nhiều để tiếp xúc với các dự án ngoài đời thực hay là dataset. Mình cũng đã gặp khá nhiều ‘các nhà khoa học dữ liệu đầy tham vọng’, những người mà hầu như không biết tí gì về:

  • Cách 1 đường ống ‘machine learning’ hoạt động 
  • Vai trò của kỹ thuật phần mềm trong kỹ năng tổng thể về khoa học máy tính 
  • Việc đặt 1 model vào khâu sản xuất/ triển khai 1 model có ý nghĩa như thế nào, v.. v..
  • Sự quan trọng của việc ‘dọn dẹp dữ liệu’ mà vốn nó đã ngốn khá nhiều thời gian của bạn

Cũng như tôi đã giới thiệu trước đó, cơ hội để chơi đùa với các bộ công cụ ‘machine learning hào nhoáng’ và các framework hiện đại nhất khá là quyến rũ cho các tân binh (và những người khác, thật sự!) 

“ Đây chính là thực tế – ngành công nghiệp này không hoạt động như thế. Có quá nhiều yếu tố để tạo ra một dự án khoa học dữ liệu gần với những gì chúng ta trải nghiệm trong các cuộc thi khoa học dữ liệu online.

Làm cách nào để bạn thu thập và lưu trữ dữ liệu, cách để chúng thực hiện ‘version control’, cách để triển khai model vào khâu sản xuất – đây chỉ là vài khía cạnh chính mà các tổ chức mong rằng bạn sẽ biết. 

Sự mong đợi lệch lạc này là thứ cản đường chính và dẫn tới việc các nhà khoa học dữ liệu từ bỏ công việc của họ. Mình luôn luôn muốn khuyên các tân binh và các nhà khoa học dữ liệu nghiệp dư có thể trao đổi liên tục với các senior và các cựu sinh viên tổ chức của họ để xóa góc khuất giữa kỳ vọng và thực tế. 

2. Định hướng vai trò của các nhà khoa học dữ liệu tới mục đích kinh doanh

Đây cũng là 1 trong những vấn đề ít phổ biến hơn. Điều này chủ yếu là do sự cường điệu quá mức về ngành khoa học máy tính và trí thông minh nhân tạo (AI) trong những năm gần đây.

Giám đốc điều hành, CxO, C-Suite, nhà đầu tư – tất cả những người có tên tuổi trong giới kinh doanh của các doanh nghiệp đều muốn thể hiện rằng tổ chức hoặc dự án của họ luôn đi đầu trong những tiến bộ công nghệ mới nhất. Và AI ngay bây giờ chính là lĩnh vực để đầu tư.

Vấn đề là – chúng ta đã thấy hàng tá các ông senior tin rằng AI chính là mấu chốt cho các vấn đề kinh doanh của họ. Và nếu họ đầu tư vào AI cùng các chuyên gia phù hợp, họ sẽ tìm ra giải pháp nhanh hơn và tiết kiệm được 1 nửa thời gian. 

5 nguyên nhân chính vì sao các nhà khoa học dữ liệu rời bỏ công việc của mình

Tuy nhiên, đó không phải là những gì sẽ diễn ra. Các dự án khoa học dữ liệu thông thường tương quan rất nhiều trải nghiệm, thử nghiệm, ‘error method’ và sự lặp đi lặp lại của cung 1 quá trình trước khi họ có thể với tới kết quả cuối cùng. Và đôi khi đòi hỏi hàng tháng trời để có thể đạt được kết quả mong muốn.

” Data Warehouse và cơ sở hạ tầng AI đều yêu cầu khoản đầu tư khủng (tùy thuộc vào quy mô của công ty) và các khám phá trong công việc có thể mất khá lâu cũng như việc hình thành ‘insight’ hoạt động từ những vùng dữ liệu rộng lớn cũng thường ngốn nhiều thời gian. Đây cũng là lý do tại sao các nhà khoa học dữ liệu đòi hỏi sự tiếp cận linh hoạt – 1 nơi mà họ có thể cống hiến thời gian và không gian để làm việc trên dữ liệu.

Điều này thường không đạt được thỏa thuận với các trưởng nhóm kinh doanh trong nhiều lĩnh vực. Các nhà khoa học dữ liệu cuối cùng cảm thấy khó chịu với khả năng lãnh đạo của các senior và những mong đợi không thực tế của bản thân, dẫn đến một cuộc di cư hàng loạt trong các dự án.

Làm cách nào để các nhà khoa học dữ liệu và trưởng nhóm kinh doanh có thể làm việc hiệu quả:

  • Thiết lập sự liên lạc mật thiết giữa khoa học dữ liệu và các nhóm kinh doanh. Họ cần phải có sự gắn kết và phối hợp với nhau
  • Khai thác trực giác kinh doanh và kiến thức từ các trưởng nhóm kinh doanh. Điều này có thể hoạt động 1 cách thần kỳ cho các nhà khoa học dữ liệu
  • Cùng phát triển 1 ma trận hiệu năng có thể đo đạc được cho kinh doanh để đo đạc sự tiến triển hiệu năng của các nhà khoa học dữ liệu
  • Sự linh hoạt đóng vai trò thiết yếu và là 1 trong những tố chất quan trọng cho 1 nhà khoa học dữ liệu

Tôi sẽ rất khuyến khích các chuyên gia khoa học dữ liệu và các trưởng nhóm kinh doanh xem thử series bên dưới của Tiến sĩ Om Deshmukh. Ông ta đã lập nên khuôn khổ cho việc vận hành thành công 1 dự án khoa học dữ liệu 1 cách rất chi tiết:

3. Sự thiếu hụt việc nâng cao kĩ năng cho các chuyên gia khoa học dữ liệu 

Ai mà không thích những thử thách mới? Tôi sẽ tranh luận rằng lĩnh vực khoa học dữ liệu sẽ chín muồi hơn nhờ những gì thử thách mang tới, như việc tạo nên tiến độ nhanh hơn, dẫn đến sự tiến bộ. Hãy chọn tên miền Natural Launguage Processing (NLP) chẳng hạn. Số lượng các tiến triển đã xảy ra trong vòng 2 năm qua thật sự rất ấn tượng. 

Hầu hết các khoa học dữ liệu sẽ rất thích làm việc với những kỹ thuật và framework mới. Ý mình là, ai mà tận hưởng được việc dựng lên rồi cứ lặp đi lặp lại trên cùng model đệ quy logistic (logistic regression model) suốt mấy năm trời đây chứ?

“ Vai trò của các nhà khoa học dữ liệu không tránh khỏi yếu tố trì trệ. Có 1 bức tường ngăn cản bạn sẽ gặp phải trong 1 thời điểm nhất định và cảm giác mong muốn các thử thách mới sẽ luôn sục sôi trong bạn. 

5 nguyên nhân chính vì sao các nhà khoa học dữ liệu rời bỏ công việc của mình

Hãy thêm điều này, về 2 yếu tố chúng ta đã nhắc ở trên về việc lo liệu các mong đợi. Đây là một sự pha trộn mọi thứ khá nhức nhói, phải không nào? Không thể tránh được viêc bất cứ nhân viên nào cũng sẽ chịu đựng sự thiếu động lực sau 1 thời điểm nhất định.

Điều này đặc biệt đúng tại các công ty lớn nơi độ linh hoạt thấp. Mình chắc rằng rất nhiều trong số các bạn đã trải nghiệm điều này, nhất là với những người đã làm việc tại các công ty ‘blue-chip. Các startup và doanh nghiệp cỡ vừa vẫn tốt hơn về vấn đề này (nhưng họ cũng đưa ra một loạt các thử thách khác nhau).

Có 3 lý do chính mà mình đã gặp phải, vốn dẫn đến việc bào mòn nhân sự:

  • Sự thiếu hụt cơ sở hạ tầng: Đây là trường hợp phổ biến với hầu hết các doanh nghiệp, họ thiếu hụt cơ sở hạ tầng như hệ thống máy tính, độ tiếp cận tới các bộ công cụ v..v.. để hỗ trợ vai trò của 1 nhà khoa học dữ liệu
  • Phạm vi kinh doanh: năng lực hoạt động của doanh nghiệp có thể hơi hạn chế và hạn hẹp. Tới 1 thời điểm, nó có thể gây khó khăn cho 1 nhà khoa học dữ liệu để suy luận nhiều insight hơn từ dữ liệu
  • Sự thiếu hụt nghiên cứu và phát triển: Là 1 nhà khoa học dữ liệu bạn sẽ thích khám phá nhiều lĩnh vực bên ngoài phạm vi công việc của bạn. Ví dụ: nếu bạn là 1 chuyên gia Thị giác Máy tính (Computer Vision) và muốn nghiên cứu thêm về NLP rồi tới vùng R&D sẽ là nơi tốt nhất cho bạn. Hầu hết các công ty điều thiếu sót điều này và dẫn đến việc hao hụt.
4. Không có quy chuẩn rõ ràng trong các khoản chi trả lương

À ha, mình có thể thấy ánh mắt chói lòa của bạn khi đọc tới tiêu đề này. Lương bổng là 1 trong những lý do chính khiến mọi người muốn nhảy vào ngành khoa học dữ liệu và làm việc như 1 sự nghiệp toàn thời gian.

Chúng ta thường thấy bảng các báo cáo từ McKinsey, Glassdoor v…v..  nơi họ giới thiệu mức lương trung bình cao vút dành cho các nhà khoa học dữ liệu. Hầu hết các tay mơ sẽ phải động lòng một khi thấy các con số được đưa ra trong các bảng báo cáo.

5 nguyên nhân chính vì sao các nhà khoa học dữ liệu rời bỏ công việc của mình

Lương của 1 nhà khoa học dữ liệu thì cao ngút trời. Mình đảm bảo rằng bạn đã đọc các bản tin trong năm nay. Và thấy các nhà khoa học dữ liệu hàng đầu đang được săn lùng bởi các công ty như Google và Apple (Ian Goodfellow là 1 ví dụ)

Điều này đang thường xuyên xảy ra. Các nhà khoa học dữ liệu, những người đang làm công việc đặc biệt trong lĩnh vực tương ứng của họ, thường được truy tìm bởi hơn 500 công ty Fortune, nơi đưa ra mức lương khá cao trong khi các công ty vừa và nhỏ thì lại không đưa ra được mức lương cao lắm (thường là vậy).

Mình cảm thấy đã đến lúc cần tiêu chuẩn hóa / quy chuẩn khi nhắc tới việc bù đắp xứng đáng. Ngay cả trong các công ty dạng vừa, nơi cần phân biệt rõ ràng khi so mức lương của 1 tân binh với kỹ năng cao đối với 1 nhà khoa học dữ liệu có kinh nghiệm với cùng cấp độ kỹ năng. Không quy chuẩn hóa mức lương có thể dẫn đến: 

  • Không hài lòng, ảnh hưởng hiệu năng làm việc ngay cả đối với 1 nhân viên có tiềm năng cao
  • Nguyên nhân chính cho việc các nhân viên tiêm nhiễm nhau trong văn phòng, xem xét về các cơ hội làm việc tốt hơn ở các nơi khác

1 lần nữa – khía cạnh này thật ra không khác nhiều so với các công việc khác, phải không nào? 

5. Tiếp xúc nhiều với các dự án khoa học dữ liệu khác nhau trên các nền tảng khác nhau

Bạn sẽ ước gì nhiều hơn giữa 2 sự lựa chọn này:

  • Lựa chọn 1: 1 công việc nhẹ – lương cao nơi bạn có thể điều chỉnh các kỹ năng và kết quả để đạt được các mục tiêu công ty, hay là
  • Lựa chon 2: 1 cuộc sống với công việc đầy linh hoạt nơi bạn có thể làm việc bất cứ đâu và đạt được sự tự trưởng thành cao?

Hầu hết các bạn đều chọn Lựa chọn 2. Ai mà không thích sự linh hoạt tại nơi làm việc và thoải mái tự chọn điều bạn muốn làm? 

5 nguyên nhân chính vì sao các nhà khoa học dữ liệu rời bỏ công việc của mình

Ngày nay, có khá nhiều sự lựa chọn cho 1 nhà khoa học dữ liệu để lựa chọn:

  • Họ có thể thử vận may của mình tại các cuộc thi trên các nền tảng như Kaggle, Analytics Vidhya v..v.. và thắng các khoản tiền thưởng thú vị và danh vọng trong cộng đồng
  • Các freelancer được yêu cầu khá nhiều do những công ty ngày nay thường có hứng thú với các dự án ngắn hạn 
  • Các nhà khoa học dữ liệu freelance biết hầu hết về Spark, Hadoop, Hive, Pig, SQL, Neo4J, MySQL, Python, R, Scala, TensorFlow, NLP, Computer Vision hay bất kỳ thứ gì của machine learning bởi vì họ phải nhảy vào các vấn đề và khám phá ra cách giải quyết nó
  • Viết blog và thương hiệu cá nhân cũng là sự lựa chọn đang khá ‘hot’ trong thời điểm này đối với các nhà khoa học dữ liệu. Chẳng hạn như là Grant Sanderson – ổng là 1 trong những người mà mình yêu thích !

Các tổ chức thường không thể đề nghị hết những điều này cho các chuyên gia khoa học dữ liệu thường trực về các lý do logistic hiển nhiên hay những lý do liên quan tới dự án. 

Làm cách nào để các công ty có thể giữ được các Chuyên gia khoa học dữ liệu hàng đầu của mình?

5 nguyên nhân chính vì sao các nhà khoa học dữ liệu rời bỏ công việc của mình

Đây là 1 vài cách mình đã thử và kiểm nghiệm, thứ mà 1 doanh nghiệp có thể duy trì hầu hết các tài năng khoa học dữ liệu của mình:

  • Tạo ra 1 môi trường học tập mạnh mẽ: Đây là điều thiết yếu cho sự phát triển cá nhân và chuyên nghiệp hóa của 1 cá thể. Lĩnh vực này đang bùng phát với nhiều thứ mới mẻ để khám phá hơn mỗi ngày và với tốc độ này sẽ rất quan trọng để cung cấp một môi trường học tập tiến bộ cho các nhà khoa học dữ liệu
  • Xây dựng 1 đội ngũ Nghiên cứu và Phát triển mạnh mẽ: Tạo 1 đội ngũ R&D có thể tạo điều kiện cho việc nghiên cứu chất lượng và có thể thực hiện được trong lĩnh vực. Cho phép các nhân viên điều hướng nghiên cứu về các chủ đề chuyên sâu cũng là 1 công thức tuyệt vời
  • Tiêu chuẩn hóa sự bù đắp của họ: Tiêu chuẩn sự bù đắp sẽ chiếm từ từ được lòng tin và cho các nhà khoa học sự đảm bảo rằng họ đang được hưởng thù lao xứng đáng dựa vào các tiêu chuẩn tốt nhất của ngành công nghiệp này (cũng dễ hiểu thôi, điều này khá khó để thực hiện được)

Lời cuối

Mọi thứ về lĩnh vực khoa học dữ liệu đều rất là năng động. Chúng ta vẫn đang cố tìm hiểu rất nhiều thứ nên việc sắp đặt trên 1 khía cạnh hay 1 quá trình hay cấu trúc vẫn cho thấy nhiều sự khó khăn cho các doanh nghiệp.

Ngày qua ngày, mình tin rằng chúng ta sẽ có những hệ thống và tiến triển mạnh mẽ đúng lúc, và các nhà khoa học dữ liệu sẽ có 1 môi trường làm việc trọn vẹn. Quan điểm này cần phải thực hiện, cả từ quan điểm kinh doanh cũng như của các nhà khoa học dữ liệu

Mình cũng rất muốn nghe về các quan điểm của bạn về điều này. Không biết bạn có đang làm việc về các vấn đề khoa học dữ liệu không? Liệu bạn đã từng trải nghiệm bất cứ vấn đề nào mình đề cập ở trên không? Hay bất kỳ vấn đề nào khác mà bạn muốn chia sẻ? Hãy cho chúng mình biết ở phần ‘bình luận’ bên dưới nhé! 

Topdev via Analytics Vidhya

Đừng quên xem thêm:
Học ngôn ngữ gì cho Data Science?
Top các khóa học Computer Science, Programming, Data Science MIỄN PHÍ cần học ngay
Top 15 thư viện Python tốt nhất cho Data Science

Cơ hội việc làm Data science hấp dẫn tại TopDev đang chờ bạn!

 

 

 

 

 

Tư duy lập trình phục vụ cuộc sống

Bài viết được chia sẻ tại Spiderum
Cho đến thời điểm này, một trong những quyết định sáng suốt nhất cuộc đời mà tôi có thể khẳng định được với cá nhân mình, đó là đi học lập trình. Dĩ nhiên tôi không phải là lập trình viên, và cũng không có ý định trở thành lập trình viên (mặc dù ước vọng thầm kín của tôi vẫn là một ngày được làm bá chủ C++), nhưng những bài học về giải quyết vấn đề (problem-solving) trong cuộc sống mà việc học lập trình đã đem lại cho tôi thì giá trị phải cao gấp mấy lần việc đọc ba cái sách self-help nhảm nhí.
Mà self-help thì làm sao?
Và với bài viết này, tôi sẽ chỉ cho bạn thấy những bài học đó là gì, với hy vọng rằng chúng sẽ có ích cho bạn. Và biết đâu đấy, có thể nhờ chúng mà bạn sẽ quyết định xách đít lên và đi học lập trình thì sao?
Ngoài ra thì như đã nói, tôi không phải là lập trình viên. Thế nên trong nội dung bài viết có chỗ nào hiểu sai về concept lập trình, hy vọng được dựa trên tính hiệu quả thực tế mà chiếu cố.

Chia nhỏ vấn đề

Một trong những thứ đầu tiên mà bạn phải làm trong hầu hết mọi chương trình học lập trình, đó là flowchart. Điều này là bởi để viết được cả một chương trình lớn là không hề dễ dàng, và việc bạn cần phải làm là chia nhỏ các bài toán lớn thành các vấn đề nhỏ hơn đến hết mức có thể để bạn có thể quản lý chúng dễ dàng.
Sơ đồ hình dung các loại vòng lặp trong R
Thật vậy, đối với các lập trình viên, không giống với hình dung của nhiều người, khi được giao nhận việc phát triển một sản phẩm phần mềm, họ sẽ không bắt tay vào thực hiện ngay lập tức. Việc đầu tiên họ làm luôn luôn là vẽ ra một sơ đồ giải thuật với flowchart hoặc một số công cụ vẽ bản đồ tư duy khác.
Trên sơ đồ giải thuật đó, họ sẽ xác định các thuộc tính của bài toán, các phương án tiếp cận, các khả năng có thể xảy ra và thứ tự ưu tiên của các đầu việc, rồi sau đó mới xác định các đầu việc cụ thể ở từng giai đoạn, và cuối cùng mới đến thực hiện. Điều này là bởi, việc bắt tay vào thực hiện công việc, tác vụ cụ thể chỉ là những vấn đề nhỏ. Còn những câu hỏi trên diện rộng, liên quan đến cấu trúc của chương trình, khả năng mở rộng và duy trì trên đường dài của chương trình mới là những vấn đề lớn. Và chỉ cần chúng ta vội vàng hay coi nhẹ việc giải quyết những vấn đề này sớm, thì càng về đường dài, hậu quả của những sai lầm này sẽ ngày càng nặng nề. Khái niệm này thường được gọi là nợ kỹ thuật (technical debt).
Tương tự như trong cuộc sống hay trong công việc của bạn. Mỗi khi bạn phải đối mặt với một vấn đề phức tạp, ví dụ như một kế hoạch xây nhà, mua nội thất, trả nợ, đặt thiết kế và in áo đồng phục cho hội nhóm hay tổ chức của bạn, lên kế hoạch đi chơi cuối tuần cho gia đình, quản lý các tác vụ trong một dự án của công ty, v.v. thì việc đầu tiên mà bạn cần làm là ngồi xuống, làm một tách trà ăn một miếng bánh, và vẽ một sơ đồ từng bước các đầu việc cụ thể mà bạn cần phải làm, các vấn đề nhỏ mà bạn cần phải giải quyết, thứ tự giải quyết thế nào để tiết kiệm thời gian và chi phí, và các phương án dự phòng ra sao. Điều này sẽ giúp bạn dự đoán trước được phần lớn các khả năng có thể xảy ra khi giải quyết vấn đề ở mọi thời điểm, và đưa ra được phương án giải quyết cần thiết, để tránh phải gánh nợ.
Một ví dụ về nợ kỹ thuật trong cuộc sống:
Bạn vừa chuyển ra khỏi chỗ ở cũ, và đang cần tìm một căn nhà để thuê ở. Công ty bạn làm việc nằm ở Mai Dịch, còn người yêu của bạn thì nhà lại ở Hà Đông. Bạn quyết định tìm thuê một căn nhà ở gần khu vực Hà Đông, để tiện còn chạy qua chạy lại chỗ người yêu, cuối tuần còn đi chơi muộn không lo đưa đón ngược đường. Xác định là sẽ ở đây lâu dài, bạn quyết định thuê nhà với kỳ hạn hợp đồng là 1 năm, đồng thời đầu tư lắp đặt điều hòa tủ lạnh cho đầy đủ tiện nghi. Tuy nhiên chỉ hai tháng sau khi bạn chuyển về chỗ ở mới thì bạn và người yêu chia tay. Giờ thì lợi ích của việc ở gần nhà người yêu đã mất, đồng thời mỗi sáng bạn phải mất đến gần 50 phút đi đường để di chuyển từ Hà Đông đến Mai Dịch, vừa tắc đường vừa nóng vừa bụi bặm. Bạn vừa đi vừa nghĩ đến cảnh lúc chuyển nhà đi lại phải tháo dỡ điều hòa với tủ lạnh và thuê người chuyên chở mà thấy khổ. Đây chính là món nợ kỹ thuật mà bạn phải gánh vì đã đưa ra quyết định sai lầm trong việc chọn vị trí thuê nhà (và chọn yêu đối tượng kia.)
Và cũng giống như trong lập trình, bạn càng tỉnh táo và khéo léo khi đưa ra các quyết định ban đầu bao nhiêu, thì kết quả bạn nhận được về sau sẽ càng chuẩn xác và càng đúng tiến độ bấy nhiêu.

Giảm cảm tính

Công cụ chia nhỏ vấn đề không chỉ dừng lại ở mô hình flowchart.
Có rất nhiều khi trong cuộc đời cũng như công việc của mình, bạn phải đối mặt với hai lựa chọn khác nhau, nhưng bạn cảm thấy chúng lại cân bằng nhau. Lựa chọn nào cũng có những cái lợi cũng như những cái bất lợi của nó, và bạn không biết làm thế nào để đặt được chúng lên bàn cân, bởi đơn giản vì chúng không có cùng một đơn vị đo và một hệ quy chiếu.
Nhưng có một tin mừng là chúng thực ra không cần thiết phải cảm tính đến thế. Việc chúng ta có thể làm, như đã nói, đó là chia nhỏ vấn đề cảm tính lớn ra thành những vấn đề cảm tính nhỏ hơn.
Tôi lấy một ví dụ cụ thể:
Bạn đang đứng trước hai sự lựa chọn khó khăn về sự nghiệp. Một là tiếp tục làm việc cho công ty A là công ty hiện tại của bạn, và hai là chuyển sang công ty B làm. Cả hai lựa chọn đều có những điểm mạnh cũng như điểm yếu của riêng mình. Vậy thì làm thế nào để bạn đưa ra được quyết định?
Tại đây, việc bạn cần làm là lên danh sách tất cả những điểm có lợi cũng như những điểm bất lợi của từng lựa chọn đối với cá nhân bạn. Và giả sử bạn có một số đầu mục như sau:

Công ty A

Có lợi
  • Sếp là chuyên gia với nhiều năm kinh nghiệm trong ngành.
  • Được sếp hứa hẹn sẽ training trực tiếp 1-1 nếu ở lại.
  • Môi trường làm việc startup trẻ trung, vui vẻ và thoải mái. Các đồng nghiệp đều rất tâm đầu ý hợp với mình.
  • Thời gian di chuyển đến công ty mỗi sáng là 15 phút.
Bất lợi
  • Lương thấp hơn khoảng 20% so với với nhu cầu sinh hoạt.
  • Có thông tin cho rằng lời hứa của sếp là không thực sự đáng tin cậy.
  • Áp lực công việc sẽ rất lớn nếu như sếp không thể thực hiện được lời hứa training, và mình sẽ mất thêm ít nhất là 3-6 tháng nữa mới có thể thấy được kết quả cho quyết định của mình nếu ở lại. Và cơ hội sang bên công ty B sẽ khép lại chỉ trong vòng khoảng 1 tháng nữa.

Công ty B

Có lợi
  • Chắc chắn sẽ được training trực tiếp vì công ty B đã xây dựng xong quy trình training rất quy củ, mặc dù người train không có nhiều kinh nghiệm bằng sếp bên công ty A.
  • Mức lương cao hơn 20% so với nhu cầu sinh hoạt.
  • Sản phẩm của công ty này phù hợp với tiêu chuẩn đạo đức của bạn hơn so với sản phẩm của công ty A.
  • Địa điểm công ty rất gần với chỗ làm của người yêu bạn và bạn có thể dành thời gian đi ăn cùng nhau vào buổi trưa.
Bất lợi
  • Vì là công ty lớn nên môi trường làm việc rất khắt khe, yêu cầu nhân viên phải mặc đồng phục vào Thứ Hai và Thứ Sáu. Nếu đến muộn quá 15 phút vào buổi sáng, bạn sẽ mất luôn chấm công của sáng hôm đó. Ngoài ra còn có khả năng bạn sẽ phải quan tâm đến chính trị chốn văn phòng.
  • Thời gian di chuyển đến công ty mỗi sáng là 35 phút, lại đi qua một đoạn đường rất bụi và không có cây xanh.
Sau khi đã có được danh sách tất cả các điểm ưu và điểm nhược của từng lựa chọn mà bạn có thể nghĩ ra được như trên, bạn tiến hành tính điểm cho từng đầu mục dựa trên giá trị ưu tiên của cá nhân bạn (hay nói cách khác là cảm tính) trên thang điểm từ -5 đến 5 (hoặc cũng có thể là một thang điểm nào đó khác như -7 đến 7 hay -10 đến 10, miễn là thang điểm này có tâm tại 0; cá nhân tôi cho rằng thang điểm -5 đến 5 là hợp lý nhất). Các điểm có lợi sẽ là điểm số dương, và các điểm bất lợi sẽ là điểm số âm.
Cuối cùng tính tổng lại, bạn sẽ có một bảng như sau:
Bảng tính điểm các đầu mục của hai lựa chọn
Sau khi đã thu về kết quả tổng điểm, bạn có thể thấy lựa chọn công ty B có sức nặng hơn lựa chọn công ty A là 2 điểm, và do đó có lẽ bạn nên lựa chọn công ty B. Hai điểm là một con số không lớn, mà nếu bạn không chia nhỏ các đầu mục của mỗi lựa chọn ra và đưa chúng trở về cùng một hệ quy chiếu là thang điểm từ -5 đến 5, thì bạn sẽ khó lòng có thể nhận ra được sự khác biệt rất nhỏ này. Mặc dù về bản chất, các con số này cũng đều mang tính cảm tính và không thể chuẩn xác 100%, nhưng chí ít là chúng tỏ ra rõ ràng hơn là hai lựa chọn đơn sơ giữa công ty A và công ty B ban đầu.

Dán nhãn & phân bổ mọi thứ

Một trong số các công cụ cơ bản để xây dựng một chương trình là các biến (variable). Biến cũng có nhiều loại biến, được phân biệt bằng các kiểu dữ liệu (data type), ví dụ như kiểu nhị phân đúng-sai là boolean, kiểu ký tự là char, kiểu số nguyên là int. Thế rồi bạn cũng có các công cụ không phân biệt bằng kiểu dữ liệu như biến, mà lại phân biệt bằng cấu trúc như lệnh If-Else, vòng lặp, mảng, struct, hàm, v.v. Việc bạn lựa chọn đúng công cụ để xử lý đúng loại đối tượng là vô cùng quan trọng, bởi nó sẽ xác định tính hiệu quả trong khả năng xử lý vấn đề của bạn.
Tương tự như trong cuộc sống. Nếu “Kiểu dữ liệu” là chất lỏng thì phải đựng bằng chai, còn nếu là thủy tinh dễ vỡ thì phải để vào hộp xốp. Giấy tờ cá nhân quan trọng thì phải cất vào cùng một nơi, còn giấy tờ đã không còn giá trị gì thì có thể sử dụng để làm nháp. Nếu thời gian không phải là lựa chọn cơ cấu đúng đắn, thì phải chọn lấy nhân lực. Nếu crush thuộc tuýp người lãng mạn thích đọc sách, thì mình phải tiếp cận bằng con đường văn thơ chữ nghĩa, thay vì bằng con đường phú quý và thẻ VISA. Nếu nhân viên thuộc dạng người có tham vọng, thì phải lấy cái sản phẩm hay con đường sự nghiệp làm công cụ khuyến khích họ, thay vì chỉ chăm chăm lương thưởng.
Mỗi một quyết định “dán nhãn & phân bổ” đúng đắn mà bạn đưa ra, là bạn đã giúp cho cuộc đời được thêm phần gọn gàng và dễ quản lý hơn cho chính mình.

Tối giản ở đâu và bao nhiêu

Hay thực ra câu hỏi là chúng ta có nên hiểu biết về chính trị? Và biết bao nhiêu là đủ?
Đối với một người lập trình viên, thì một trong những câu hỏi đau đầu nhất và cũng yêu cầu kinh nghiệm trong nghề dạn dày nhất, đó chính là việc khi nào thì nên trừu tượng hóa (abstraction), và bao nhiêu lớp trừu tượng hóa là đủ.
Các cấp độ trừu tượng trong lập trình
Ở cấp độ cơ bản nhất trong lập trình, chúng ta biết rằng có một đoạn code thực hiện một tác vụ cụ thể và nhất định—nhận vào một tham số và trả về một kết quả—mà ta sẽ sử dụng lại nhiều lần, thì chúng ta có thể thiết lập để đoạn code đó để nó trở thành một “hàm.” Và hàm này sẽ hoạt động như một module riêng biệt: nó nhận đầu vào và trả về đầu ra. Đó được gọi là trừu tượng hóa.
Việc trừu tượng hóa này này đem lại một số lợi ích, và một trong số đó là việc bạn có thể yên tâm rằng mỗi khi bạn cần đến chức năng của đoạn code này, bạn chỉ việc gọi hàm đó ra, đưa cho nó những tham số nó cần, và nó sẽ trả về cho bạn kết quả mà bạn yêu cầu, và bạn sẽ không cần phải quan tâm xem nó hoạt động như thế nào nữa. Ta có thể tạm hình dung rằng, việc hàm đó hoạt động như thế nào là vấn đề bậc thấp (low-level), còn việc ta sử dụng công cụ đó để giải quyết công việc của mình thế nào là vấn đề bậc cao (high-level).
Trong cuộc sống cũng tương tự. Mỗi một tiên đề, mỗi một giả định, mỗi một niềm tin mà chúng ta có về bản chất của một sự vật hay một sự việc, cũng là một lớp trừu tượng hóa như vậy. Ví dụ:
Bạn biết rằng giá mỗi lít xăng rơi vào khoảng 20.000đ, do đó nếu hôm nay đổ xăng chúng ta có thấy giá xăng ở mức 20.520đ/lít, thì bạn có thể tạm tin rằng đó là giá xăng đã được nhà nước ban hành, chứ không phải là trạm xăng có ý định lừa bạn.
Bạn ngồi ở một tiệm cà phê, và có một người đàn ông đến hỏi bạn có muốn đánh giày hay không. Bạn bảo rằng có, thế là ông ta đưa cho bạn một đôi dép khá cũ nát để đi tạm, rồi cầm đôi giày của bạn đi mất. Ba tiếng sau không thấy ông ta quay lại, bạn xác định rằng ông ta đã lấy cắp đôi giày của bạn.
Định lý Pythagoras đã chứng minh rằng bình phương cạnh huyền bằng tổng bình phương hai cạnh góc vuông, do đó để tính ra cạnh huyền thì bạn chỉ cần tính được hai cạnh góc vuông là xong, khỏi phải chứng minh lại.
Bạn biết rằng kể từ năm 1986, Việt Nam đã chuyển từ cơ chế kinh tế kế hoạch hóa tập
trung bao cấp sang cơ chế kinh tế thị trường, cho phép các doanh nghiệp tư nhân tồn tại và cho phép bàn tay của thị trường thúc đẩy kinh tế. Từ đó kinh tế Việt Nam phát triển, đời sống nhân dân được cải thiện. Bạn thấy rằng như thế này là đủ, không cần phải quan tâm gì nữa làm gì cho phiền.
Bạn có thể thấy rằng, những giả định trên giúp cho bạn được dễ dàng hơn trong cuộc sống, thay vì phải suy nghĩ và kiểm chứng cho từng khả năng mà bạn có thể nghĩ ra một, bởi vì sức người và thời gian của bạn là có hạn. Bạn không thể cứ mỗi lần cần dùng là lại phải chứng minh lại định lý Pythagoras, hay bạn phải kiểm chứng khả năng rằng có lẽ người đàn ông kia trong lúc đánh giày của bạn đã gặp phải tai nạn bởi một người lái xe say rượu. Nếu như không có trừu tượng hóa, hay nói cách khác là tạm chấp nhận rằng một số điều là đúng mặc dù chưa có đủ cơ sở, thì bạn sẽ không thể mua nổi một mớ rau 5.000đ ngoài chợ.
Tuy vậy, bạn cũng có thể thấy, hoặc là không, rằng một trong số những giả định nêu trên là không hợp lý cho lắm.
Và đó cũng chính là vấn đề của việc trừu tượng hóa. Nó chỉ cho bạn nhìn thấy bề nổi để bạn có thể sống dễ dàng hơn và tập trung vào những gì quan trọng với mình. Thế nhưng điều đó cũng đồng nghĩa rằng nó đã trở thành một lớp vỏ bọc phức tạp để che giấu đi cái bề chìm. Và nhỡ như có một vấn đề nào đó có nguồn gốc xuất phát từ bề chìm, thì sẽ rất khó để bạn có thể truy ra được nguồn gốc của nó. Và vì thế, bạn cần phải biết đưa ra một lựa chọn hết sức thông minh trong việc lúc nào thì cần trừu tượng hóa, và lúc nào thì không. Tương tự trong cuộc sống cũng vậy: biết giả định rằng cái gì cái gì về cơ bản là đúng, không cần phải nghĩ nữa, và cũng hiểu rằng cái gì đòi hỏi chúng ta phải có cái nhìn sâu sắc hơn.

Nếu những vấn đề thường ngày của bạn là những vấn đề bậc cao, thì chính trị là những vấn đề bậc thấp. Bởi chỉ khi chính trị ổn, thì cuộc sống của bạn mới êm đẹp để bạn tập trung lo sống.

Nhiều bạn trẻ cũng hỏi tôi, giống như tôi cũng thường tự hỏi chính mình, rằng em có nên quan tâm đến chính trị hay không, và quan tâm bao nhiêu là đủ. Và mặc dù tôi ghét phải trả lời thế này, nhưng sự thực là bao nhiêu cũng không đủ. Cũng giống như code bậc thấp trong lập trình, bạn càng biết sâu thì bạn càng có lợi. Tuy nhiên, chi phí thời gian và công sức là không hề nhỏ, và do đó vấn đề chính bạn cần lưu ý là cân bằng nó với bản thân, với cuộc sống và với công việc hàng ngày của bạn như thế nào.

Thói quen ghi chép

Trong lập trình, nhất là khi phải làm việc trong một dự án dài hơi, khi phải làm việc với một ai đó khác, hay có người sẽ phải đến làm việc tiếp trên code của bạn, thì một trong những quy tắc sống còn trong quá trình code của bạn là comment, về cơ bản nghĩa là ghi chép.
Đừng quên comment code của bạn
Việc ghi chép này là vô cùng quan trọng. Khi bạn đang thực hiện một tác vụ nào đó với một logic rất cụ thể, nhưng bạn lại chưa có yếu tố nào khác tương tác với logic của tác vụ này, thì bạn sẽ phải tìm cách ghi chép lại logic của nó để sau này khi bạn cần phải tương tác với nó, bạn còn có thể làm được mà không phải mò mẫm xem lại xem hôm đó mình ăn cái gì mà có thể code như thế này.
Tương tự trong cuộc sống cũng có rất nhiều những trường hợp như vậy. Bạn được nhận một voucher giảm giá cho một cửa hàng mua sắm mới khai trương, và trên voucher có mã giảm giá và số điện thoại của cửa hàng. Tuy nhiên, hiện tại bạn lại đang ở chỗ làm và chưa thể thực hiện ngay được việc đến cửa hàng đó hoặc là gọi điện cho họ để đặt mua, nên bạn quyết định sẽ ghi lại mã giảm giá và số điện thoại của họ để đến tối về nhà bạn có thể thực hiện được việc bạn muốn làm. Và đó cũng tương tự như những ngày sinh nhật của bạn bè, số điện thoại của đồng nghiệp, giấy nhắc nhở công việc cá nhân, kế hoạch cho giai đoạn tiếp theo của dự án, tài liệu mô tả của bộ phận thiết kế gửi cho bộ phận kỹ thuật, v.v. Luôn ghi nhớ trong đầu rằng, khi bạn phải làm việc với một ai khác, hay là phải làm việc với chính bản thân mình trong tương lai cũng vậy, hãy dành cho họ một sự cảm thông sâu sắc và ghi chép lại để trang bị cho họ những thông tin cần thiết để họ có thể làm việc hiệu quả được với bạn.
Mặt khác, đôi khi bạn cũng cần phải lưu ý cách bạn ghi chép hay là khối lượng bạn ghi chép. Bởi trong lập trình, dù ít hay nhiều thì khi bạn comment dài dòng quá lố, compiler cũng phải mất thời gian đi qua comment của bạn (mặc dù nó sẽ bỏ qua), do đó phần nào làm chậm thời gian xuất chương trình. Cũng giống như việc bạn ghi chép lại vào điện thoại hay vào sổ tay vậy. Dung lượng của chúng có hạn, và thời gian ghi chép hay đọc lại của bạn cũng có hạn, vì thế hãy ghi chép sao cho thật thông minh, rõ ràng, không thiếu cũng không thừa.

Giải phóng bộ nhớ khi không dùng

Khi xây dựng một chương trình, bạn luôn luôn cần phải theo dõi khối lượng đối tượng, dữ liệu, cũng như các vùng bộ nhớ trên hệ thống máy tính hay các loại tài nguyên hệ thống khác, và nhanh chóng giải phóng những gì mà chương trình không còn cần sử dụng nữa ra khỏi bộ nhớ, để dành nó cho các tác vụ tiếp theo.
Hãy tự dọn bàn của mình trước khi bạn crash
Bạn có thể hình dung ổ cứng máy tính của bạn là cái nhà kho, còn bộ nhớ của bạn là cái mặt bàn. Hoặc cũng có thể ổ cứng máy tính là nhà bạn, còn bộ nhớ là chỗ làm của bạn. Hay như chính trên máy tính của bạn, thì theo một cách nào đó, màn hình desktop cũng là một dạng bộ nhớ của bạn. Bộ nhớ là nơi bạn tạm để những tác vụ, những công cụ, cũng như các thành phần của công việc hiện tại đang dang dở và yêu cầu phải có quyền truy cập hay khả năng tiếp cận cao với các đối tượng này. Thế nhưng bạn cần phải nhớ rằng bộ nhớ của bạn chỉ là tạm thời, và vì thế nó có giới hạn. Khi bạn đã hoàn thành một công việc nào đó, hãy nhớ đến việc dọn dẹp rác trên bộ nhớ. Thứ gì không cần nữa thì hãy trả lại về đúng nơi lưu trữ. Chỉ có thể bằng cách đó, tâm trí của bạn mới có thể được giải phóng để các tác vụ sau được giải quyết hiệu quả hơn. Vả lại, bản thân việc giải phóng bộ nhớ và nhìn nó được gọn gàng, sạch sẽ, và quan trọng nhất là tối ưu, cũng là một cách để đem lại cảm giác rằng bạn đã hoàn thành công việc xuất sắc vậy.
Dẫu biết rằng các ngôn ngữ lập trình bậc cao hiện nay như C# đã có khả năng tự động quản lý bộ nhớ (garbage collection), nhưng bài học thực tế của việc giải phóng bộ nhớ vẫn luôn mang giá trị cao.

Lời kết

Tôi sẽ không ngần ngại khi nói rằng lập trình quả thực xứng đáng là một môn cần được đưa vào giảng dạy mở rộng ở cấp phổ thông, bởi những công cụ mà nó đem lại cho chúng ta trong việc tư duy về giải quyết vấn đề là vô cùng hữu dụng, dù cho bạn có là ai đi chăng nữa. Bởi khi và chỉ khi bạn bắt buộc phải làm việc với những giới hạn, thì từ đó mới có thể sinh ra được những cách giải quyết tối ưu.
  50 keywords mà mọi JAVA developer nên biết
  Một vài lỗi mà những lập trình viên mới có thể mắc phải
  13 kênh Youtube lập trình tiếng Việt giúp bạn trở thành Fullstack developer
Có thể bạn quan tâm:

5 công cụ nguồn mở các thư viện cần biết

Đã có thời khi làm việc trong thư viện tôi đã thấy rất khó chịu (như nhiều thủ thư khác) vì đã có quá ít lựa chọn về phần mềm thực sự làm được những gì tôi cần. Trong các thư viện chúng tôi đã quá quen thuộc với mô hình người bán hàng = phần mềm. Ở những nơi một nhà bán hàng kiểm soát sản phẩm và trong khi có thể có những sản phẩm tương tự khác, thì chúng cũng bị một nhà bán hàng khác kiểm soát.

Điều này giải thích vì sao các thư viện cần phải nhìn sâu sát hơn phần mềm nguồn mở.

Bằng việc loại bỏ “chủ sở hữu” (có nghĩa là nhà bán hàng) khỏi phương trình ở trên, chúng tôi có được nhiều sự tự do để tạo ra phần mềm làm những gì chúng tôi muốn, cách chúng tôi muốn, khi chúng tôi muốn. Một trong những điều khó khăn nhất để dạy các thư viện đang chuyển sang giải pháp nguồn mở là sức mạnh bây giờ nằm trong tay của họ để chỉ huy phần mềm.

Vì lý do đặc biệt này, tôi dạy nhiều lớp tập huấn về phần mềm nguồn mở cho các thư viện, và tôi luôn thấy nó thú vị khi tôi mang tới các công cụ mà những người tham dự chưa bao giờ nghe thấy về chúng. Đúng là khó để bắt kịp tất cả các ứng dụng có ngoài đó, nên tôi đã biên dịch một danh sách lớn với 5 công cụ nguồn mở mà nhiều thư viện hơn nên biết về chúng.

SubjectsPlus

SubjectsPlus là công cụ chỉ dẫn đối tượng nguồn mở. Đối với các dạng không phải thư viện đọc điều này: chỉ dẫn đối tượng là một tài nguyên chung trong các thư viện để chỉ cho mọi người tới các tài nguyên thích hợp về một chủ đề cụ thể. Khi tôi lần đầu làm việc trong các thư viện, những gì chúng tôi từng làm việc với từng là hàng loạt các trang được viết mã cố định với đầy các đường liên kết. Bây giờ chúng tôi có các công cụ như SubjectsPlus để làm giảm nhẹ công việc nặng nhọc đó cho chúng tôi.

SubjectsPlus dễ dàng bổ sung thêm các nhân viên (hoặc những người quản lý chỉ dẫn) và các tài nguyên (in, các cơ sở dữ liệu, các đường liên kết, và hơn thế nữa) sao cho bạn có thể xuất bản chỉ dẫn đối tượng hữu ích cho các khách hàng của bạn. Ví dụ hãy kiểm tra Chỉ dẫn Khóa học về CSE 561 (Course Guide for CSE 561) của Thư viện Đại học Oakland.

LibKi

Libki là hệ thống quản lý quầy công cộng được thiết kế cho các thư viện bởi những người đang làm việc trong thư viện! Nó cho phép bạn quản lý các máy tính công cộng của bạn trong thư viện (hoặc bất kỳ cơ sở công khai nào) với thiết lập tối thiểu.

Tôi nhớ khi thư viện đầu tiên của tôi chọn một hệ thống quản lý quầy – đó từng là sự tra tấn để thiết lập và duy trì. Đó là khi tôi đi ra ngoài tìm kiếm lựa chọn thay thế và tìm thấy LibKi.

Sử dụng LibKi, thư viện có thể quản lý những người sử dụng có thể ngồi với các máy công cộng bao nhiêu thời gian, đưa ra các mã ID của người viếng thăm với các quy tắc khác nhau so với những người có thẻ thường xuyên, để dành các máy cho các khách hàng, và quản lý chung quầy sao cho bất kỳ ai cũng có được phần thời gian công bằng của họ.

BibApp

BibApp là một mạng xã hội nghiên cứu. Đây là công cụ gọn nhẹ cho các thư viện hàn lâm sử dụng để kết nối các nhà nghiên cứu trong khu trường với các chuyên gia trong lĩnh vực để hỗ trợ họ trong nghiên cứu của họ. Các nhà nghiên cứu tạo các hồ sơ và thêm các công việc của họ vào hồ sơ của họ. Điều này làm cho dễ dàng để họ thúc đẩy công việc của họ, và nó chỉ ra phần còn lại của cộng đồng trong khu trường của bạn về nhà nghiên cứu đó đang làm về điều gì. Đối với các thư viện, BibApp làm cho dễ dàng để tìm ra nghiên cứu nào đang được tiến hành trong khu trường.

Guide on the Side

Guide on the Side là công cụ đáng kinh ngạc, và nó nói ngay trên website: Biết cách sử dụng từ ư? Bạn biết rồi cách sử dụng Guide on the Side! Đây là công cụ nhỏ hữu dụng ngồi bên cạnh website hoặc catalog thư viện của bạn để hướng dẫn các khách hàng cách sử dụng hệ thống. Xem công cụ này hoạt động ở Đại học Arizona.

Về cơ bản, bạn vết sách chỉ dẫn của bạn trong giao diện của Guide on the Side và sau đó nói cho nó URL nào phải hiển thị ở bên phải màn hình. Sách chỉ dẫn của bạn thậm chí có thể bao gồm bài kiểm tra để chắc chắn mọi người đang tuân theo cùng và hiểu các chỉ dẫn của bạn. Công cụ này có thể có nhiều sử dụng bên trong thư viện.

OpenRoom

OpenRoom cho phép bạn quản lý sự đặt chỗ trước không gian công cộng trong thư viện. Một câu hỏi lặp đi lặp lại mà tôi có trong các phiên huấn luyện là ứng dụng đặt phòng nguồn mở. Thực sự có vài ứng dụng như vậy ngoài đó, nhưng OpenRoom được thiết kế bởi và cho các thư viện. Giao diện đơn giản cho phép dễ dàng tùy biến thích nghi chủ đề, tạo sự đặt chỗ trước thông qua mẫu biểu trên web, và nhanh chóng thiết lập các phòng và/hoặc các nhóm phòng. Hãy lấy OpenRoom và thử nó.

Tác giả: Lê Minh Nghĩa

Chuyện những Pull Requests trong lập trình

Nếu bạn đã trở thành một lập trình viên, làm việc theo team, hoặc bạn đã từng sử dụng qua những công cụ quản lí mã nguồn (git, svn, …), có lẽ bạn sẽ không quá xa lạ với khái niệm về những Pull Requests (PRs). Bạn làm việc với nó hằng ngày, cùng tương tác với những đồng nghiệp của mình trên những PRs, bạn tự tạo những PRs cho mình hoặc kiểm tra những PRs của người khác, … nhưng bạn có hiểu hết hoặc tận dụng hết những ý nghĩa mà nó mang lại cho bạn?

Nếu bạn chưa biết PRs là gì, thì cũng đừng quá lo lắng, mình sẽ giải thích lại khái niệm đó, cũng như những công việc lập trình viên cần phải làm từ khi tạo ra cho tới khi kết thúc PRs ngay sau đây.

Pull request (PR) là gì?

Để nói tới PR, chúng ta không thể không nhắc tới mã nguồn (source code) của chương trình. Thông thường, một phần mềm được tạo nên bởi nhiều lập trình viên, để có thể đảm bảo tính nhất quán về source code của sản phẩm, chúng ta sẽ cần sử dụng tới những phần mềm quản lí mã nguồn, ví dụ như git hoặc svn. Trong số đó, nổi tiếng và quen thuộc nhất với cộng đồng lập trình viên hiện tại có lẽ là git và ứng dụng giúp chúng ta thực thi và tương tác với nó là github.

Khi nhiều lập trình viên cùng làm việc trên cùng một tập mã nguồn (thuật ngữ chuyên ngành gọi là repository hay ngắn ngọn là repo), họ thực hiện sao chép repo gốc – được quản lí trên server github – về máy tính cá nhân, ta gọi mã nguồn ở máy tính cá nhân là local repo. Hình dung đơn giản bằng hình ảnh sau đây:

Thông thường, mã nguồn chính của sản phẩm thường được để trong nhánh (thuật ngữ là branch) có tên gọi là master. Khi phát triển một tính năng mới, nhưng lại tránh thay đổi gì mã nguồn đang có của nhánh master, lập trình viên sẽ tạo ra các nhánh con, ví dụ: nhánh feature_A, nhánh feature_B … Sau đó sẽ thêm mã nguồn mới vào các nhánh con này, trong khi họ làm tính năng mới thì nhánh master sẽ không bị thay đổi gì cả, vì thế mà trong lúc họ làm phần mềm vẫn chạy bình thường. Minh họa bằng sơ đồ sau:

Khi lập trình viên viết code xong cho những tính năng mình phụ trách, họ sẽ tạo những Pull Request (minh họa ở trên là PR-1 và PR-2) với mục đích kĩ thuật là để gộp mã nguồn mới vào mã nguồn cũ (thuật ngữ chuyên môn gọi là merge source). Bên cạnh đó, PRs cũng để thông báo với những người làm chung rằng: tôi đã làm xong và sẵn sàng gộp chung mã nguồn mới (của tính năng mới) vào phần mềm đang chạy, để bổ sung tính năng mới cho sản phẩm.

Những tác dụng của Pull Requests

Như đã giải thích ở trên, các PRs là các khái niệm hoàn toàn mang tính kĩ thuật: giúp ta gộp chung mã nguồn mới vào mã nguồn cũ. Với một khái niệm hoàn toàn mang tính kĩ thuật như vậy, chúng ta liệu có học hỏi được nhiều bài học từ nó? Nếu bạn còn thắc mắc như vậy thì để mình nói cho bạn nghe thêm vài tác dụng khác của PRs nhé.

Nhờ người khác kiểm tra lại mã nguồn (review)

Khi bạn tạo ra một PR để yêu cầu có một sự merge source, bạn mới thực hiện được một nửa quá trình, PR còn cần phải được “xác nhận” lại lần cuối trước khi lệnh merge được chính thức kích hoạt bởi phần mềm quản lí mã nguồn git. Trong github, việc “xác nhận” này được thực hiện bằng việc nhấn vào nút MERGE trên PR.

Ở bước “xác nhận” này, bạn có thể nhờ một người khác trong nhóm của bạn -người có thể có nhiều kinh nghiệm – kiểm tra lại PR đó xem coi những đoạn mã lệnh bạn viết trong tính năng mới có ổn hay không, có thực thi đúng chức năng và nhiệm vụ không, có đạt hiệu suất cao hay không, có bảo mật hay không, … Khi những tiêu chí về chất lượng mã nguồn được kiểm tra và đảm bảo, tính năng mới (hay mã nguồn mới) mới chính thức được merge vào sản phẩm. Công việc kiểm tra này được gọi bằng thuật ngữ là review.

Từ cái nhìn trên, có thể hiểu PR là một thứ thể hiện cho kiến thức và kĩ năng của bạn, việc nhờ người khác nhận xét và kiểm tra, bạn sẽ hạn chế được lỗi (nếu có) phát sinh trong quá trình viết code, cũng như sẽ rút ra được nhiều bài học mới và vấn đề mới cần cải thiện.

Lưu lại lịch sử phát triển của sản phẩm

Sau khi PRs được merge vào nhánh chính của sản phẩm, thông tin về nó sẽ không bị mất đi. Phần mềm quản lí mã nguồn sẽ tiếp tục lưu lại thông tin về những PRs trong dữ liệu của nó, những thông tin thay đổi về mã nguồn chi tiết tới từng dòng đều được lưu lại để thực hiện truy vấn lại sau này. Nói một cách khác, quá trình phát triển của sản phẩm được ghi lại một cách rõ ràng và chi tiết thông qua những PRs.

Tất cả mọi người lớn đều đã từng là những đứa trẻ, tương tự như thế, tất cả những phầm mềm dù lớn tới và phức tạp tới đâu cũng từng được tạo nên từ những thứ đơn giản ban đầu. Mỗi PR giống như một bài học bạn học được trong quá trình lớn lên và trưởng thành vậy. Bạn có thể học được rất nhiều bài học về phát triển phần mềm từ những PRs trong quá khứ.

Là cơ hội giúp bạn học hỏi từ người khác

Bạn vẫn luôn được những lập trình viên có kinh nghiệm khuyên rằng: cách tốt nhất để phát triển kĩ năng của mình là phải làm thật nhiều. Sự thật là vậy, bạn càng viết code nhiều, luyện tập thiết kế cách tính năng khác nhau thì sẽ càng mau giỏi. Nhưng vấn đề là, khi bạn còn chưa có nhiều kinh nghiệm, leader hoặc cấp trên của bạn nào dám đưa cho bạn phụ trách những tính năng lớn, những tính năng phức tạp.

Khi bạn không được trao vai trò chính trong việc phát triển và trở thành người tạo ra những PR, bạn vẫn có thể học hỏi từ nó, bằng cách đóng góp những commits nhỏ trong PR, trở thành người kiểm tra Pull Request (gọi là reviewer), hay đơn thuần chỉ là người đọc qua những sự thay đổi của mã nguồn từ những PRs.

Khi làm việc với những nhóm lớn, sẽ có rất nhiều PRs được tạo ra trong quá trình phát triển sản phẩm, những PRs chứa đựng giải pháp cho những điều bạn có thể chưa biết, tham khảo những PRs này cũng sẽ giúp bạn học thêm được rất nhiều điều mới mẻ và có ích cho sự phát triển của bạn.

Nói tóm lại

Bạn có thể thấy, PR thực chất là một khái niệm mang tính kĩ thuật của những phần mềm quản lí mã nguồn, nó giúp ta gộp những mã nguồn mới vào mã nguồn cũ để phát triển sản phẩm, nhưng không vì thế mà nó không chứa nhiều điều cho chúng ta học hỏi.

Kĩ thuật tách nhánh vào tạo những PRs giúp chúng ta tách biệt trách nhiệm của từng người, phân chia công việc lớn thành những thứ nhỏ hơn để nhiều người cùng có thể phát triển sản phẩm mà không dẫm chân lên nhau. Hãy luôn nhớ một điều là: phát triển phần mềm là công việc của cả một tập thể, bạn luôn luôn có cơ hội để học hỏi từ những người khác, ngay cả khi bạn không làm việc trực tiếp với họ. Những Pull Requests chính là thứ giúp chúng ta thực hiện điều này.

TopDev via Những dòng code vui

Vượt qua các bài phỏng vấn Javascript

 Đối với cả ngành công nghệ phần mềm hiện nay, thì số lượng việc làm có liên quan đến lĩnh vực web chiếm một tỉ trọng tương đối lớn. Lập trình viên có khả năng làm về web đang được săn tìm khá nhiều trên các trang tìm việc online đủ để minh chứng cho điều này. Và nếu đã làm web thì không thể không nhắc tới ngôn ngữ Javascript.

Kĩ năng về Javascript không chỉ cần thiết cho công việc phát triển Client-side của hệ thống, ngoài ra với sự xuất hiện của NodeJS và nhiều Framework hỗ trợ khác, bạn có thể làm việc như một Fullstack developer với chỉ 1 ngôn ngữ duy nhất là Javascript, thậm chí là ứng dụng web của bạn còn có thể được mang lên cả các thiết bị di động cũng chỉ với Javascript, thật vi diệu ^^.

Xem việc làm javascript đãi ngộ tốt trên TopDev

Tất cả những điều đó có đủ thuyết phục để bạn bỏ công sức ra tìm hiểu về Javascript chưa? Công việc có liên quan tới Javascript tràn ngập ngoài thị trường, và nếu bạn có ý định tìm một công việc nào đó có liên quan đến ngôn ngữ này, thì bạn cần phải xem lại các vấn đề cốt lõi sau đây để có thể vượt qua được vòng phỏng vấn của các công ty tuyển dụng. Càng nắm vững kiến thức, vượt qua được các câu hỏi phỏng vấn dễ dàng bao nhiêu thì cơ hội bạn nhận được mức lương tốt cũng sẽ cao tương ứng. Chúng ta cùng xem thử các vấn đề thường gặp phải trong các bài phỏng vấn Javascript thôi nào.

1. Vấn đề đầu tiên: Scope (tầm vực) của biến.

Scope trong Javascript có một chút khác biệt so với các ngôn ngữ lập trình khác, Scope trong Javascript có phạm vi nằm trong hàm (function scope):

  • Thứ 1: Biến được khai báo bên trong hàm sẽ là biến cục bộ, và biến cục bộ thì có thể được truy cập bên trong hàm (không có khái niệm scope trong 1 khối lệnh).
  • Thứ 2: Biến được khai báo bên ngoài hàm sẽ là biến toàn cục, và có thể được truy cập từ bất cứ đâu.

Chú ý: biến khai báo bên trong hàm mà không có từ khoá “var” cũng sẽ trở thành biến toàn cục. Ngoài ra, việc khai báo “var” mà lại không nằm trong function nào thì biến cũng sẽ mang tầm vực toàn cục.

Chúng ta kiểm chứng lại ngay sau đây, đầu tiên là khai báo một hàm trong Javascript

1
2
3
4
5
6
7
8
9
function testScope()
{
    var local_var_1 = global_var_1 = "inside_function";
    if(true){
        var local_var_2 = "inside IF";
    }
    console.log("Test local_var_1 inside function: " + local_var_1);
    console.log("Test local_var_2 inside function: " + local_var_2);
}

Mọi người thử trả lời xem kết quả của câu lệnh dưới đây là gì nào?

1
2
3
testScope();
console.log("Test local_var_1 outside function: " + local_var_1);
console.log("Test global_var_1 outside function: " + global_var_1);

Dưới đây là đáp án cho câu hỏi trên, các bạn nên thử tự mình trả lời rồi so sánh với kết quả bên dưới này xem như thế nào nhé:

1
2
3
4
Test local_var_1 inside function: inside_function
Test local_var_2 inside function: inside IF
undefined
Test global_var_1 outside function: inside_function

Bằng việc thực thi hàm testScope(), chúng ta sẽ kiểm tra được giá trị của biến “local_var_2” mặc dù được khai báo bên trong khối lệnh if() nhưng nó vẫn sẽ có tầm vực truy cập bên ngoài khối lệnh này. Tầm vực của biến “global_var_1” có giá trị toàn cục, do vậy ta vẫn có thể truy cập giá trị biến này bên ngoài hàm, lí do là ta đã không dùng từ khoá “var” khi khai báo biến này (chú ý là trong trường hợp này “var” chỉ có tác dụng khai báo với biến “local_var_1” mà thôi).

Vấn đề về scope trong Javascript có đôi chút nhập nhằng, do vậy mà mình khuyên mọi người luôn dùng từ khoá var để khai báo biến nhằm tránh các tình huống biến trở thành biến toàn cục có thể dẫn tới những lỗi rất khó để phát hiện. Ngoài ra nên dùng thêm từ khoá “use strict” để sử dụng Javascript với strict mode, mode này sẽ yêu cầu bạn phải khai báo tường minh tất cả mọi thứ để tránh các lỗi tiềm tàng có thể xảy ra.

2. Khái niệm hoisting

Khi code sử dụng javascript, có một khái niệm cũng khá là đặc biệt là hoisting. Với khái niệm này, javascript quy định, mọi khai báo biến đều được đưa lên trên cùng của một tầm vực. Tức là mặc kệ bạn khai báo biến ở vị trí nào trong 1 hàm, thì tự động nó sẽ kéo lên trên cùng của hàm để khai báo (javascript tự động thực hiện ngầm cho khái niệm này).

Khái niệm hoisting này cũng là một khái niệm khác biệt của Javascript, nhiều lập trình viên thường bỏ qua vấn đề này, và đây cũng là 1 trong những nguyên nhân gây ra lỗi rất khó phát hiện ở trong ngôn ngữ lập trình này. Chạy thử đoạn code sau để hiểu rõ hơn:

1
2
3
4
5
6
var hoisting_example = 'test_hoisting_AAA';
function explainHoisting(){
    console.log(hoisting_example);
    var hoisting_example = "testing_hoisting_BBB";
    console.log(hoisting_example);
}

Chắc hẳn là nhiều người sẽ nghĩ rằng kết quả là thế này:

1
2
testing_hoisting_AAA
testing_hoisting_BBB

Tuy nhiên, đáp án ở trên là SAI. Kết quả đúng phải là:

1
2
undefined
testing_hoisting_BBB

Câu hỏi đặt ra là: tại sao ở lần đầu tiên in ra, thì biến “hoisting_example” lại không có giá trị? Câu trả lời là: chính việc hoisting trong Javascript làm như thế. Như đã nói ở trên, việc khai báo biến đã được Javascript ngầm thực hiện ở trên đầu hàm, chính điều này làm mất đi giá trị của biến “hoisting_example” đã được khai báo bên ngoài hàm. Tóm lại, đoạn code ở trên có thể được hiểu một cách tường minh như sau:

1
2
3
4
5
6
7
var hoisting_example = 'test_hoisting_AAA';
function explainHoisting(){
    var hoisting_example;
    console.log(hoisting_example);
    hoisting_example = "testing_hoisting_BBB";
    console.log(hoisting_example);
}

Ngoài ra, trong phiên bản ESMAScript 6, chúng ta có them từ khoá khai báo biến mới đó là “let”. Từ khoá “let” dung để khai báo 1 biến có tầm vực trong 1 block code, tứ là nó sẽ không ảnh hưởng bởi khái niệm hoisting, mọi người có thể tìm hiểu them trên google để hiểu rõ hơn về các đặc trưng mới của phiên bản ECMAScript 6. Và hãy nhớ, để ý vấn đề hoisting khi làm việc với Javascript nha mọi người!

3. Về Native method, khái niệm Object và Prototype của Object

Chúng ta thử chạy đoạn lệnh dưới đây và giải thích kết quả xem nào:

1
2
3
4
5
var num = 5;
typeof(num);          //”number”
//chạy lệnh lấy bình phương của số đó
num.square();        // Uncaught TypeError: num.square is not a function

Mọi người thử trả lời câu hỏi tại sao lệnh lấy bình phương square() lại báo lỗi? Nếu bạn cho rằng kiểu dữ liệu number không có sẵn phương thức square() thì bạn đã trả lời đúng rùi đấy. Vậy câu hỏi đặt ra tiếp theo là: làm thế nào để ta khiến cho câu lệnh đó thực thi đúng như mong đợi? 1p suy nghĩ bắt đầu ^^

… … … … Hết giờ … … …

Có thể là giải pháp của bạn trông giống như sau:

1
2
3
4
Number.prototype.square = Number.prototype.square || function(){return this*this;};
//Gọi thử lệnh để xem kết quả xem nào:
num.square();               //25

Công việc chúng ta vừa làm ở trên là: xây dựng thêm một native method cho kiểu dữ liệu number, đây thực chất là việc thêm một phương thức prototype cho đối tượng built-in là Number.

Như bạn đã thấy, để có thể đưa ra giải pháp ở trên, chúng ta cần các kiến thức về khái niệm Object và prototype của Object trong Javascript. Những thuộc tính và phương thức trong prototype của object sẽ được các object con kế thừa. Đây chính là 1 đặc điểm hết sức khác biệt nữa của Javascript: nó là một ngôn ngữ sử dụng kế thừa kiểu object-based (không giống class-based như các ngôn ngữ OOP truyền thống).

Các bạn cần phải chuẩn bị kĩ các kiến thức về Object và khái niệm prototype của object nếu muốn trở thành một lập trình viên Javascript tốt. Hôm nào có thời gian mình cũng sẽ ôn lại các kiến thức đó qua một bài viết mới. Các bạn nhớ đón xem nhé ^^

4. Con trỏ this

Con trỏ this được sử dụng rất nhiều trong các đoạn mã JS, và nó cũng là một trong những khái niệm gây ra nhiều sự hiểu lầm (dẫn đến bug) nhất trong ngôn ngữ này. Để lập trình tốt bằng Javascript thì người lập trình viên buộc phải hiểu rõ cách mà con trỏ this vận hành.

Trong các ngôn ngữ OOP điển hình như C++, PHP, Java, … khái niệm con trỏ “this” tương đối dễ hiểu, nó gắn liền với thực thể (instance) đang được kích hoạt. Ở javascript thì mọi chuyện có vẻ phức tạp hơn, giá trị của this gắn liền với context mà nó được gọi, xét thử đoạn code sau:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var name = "Peter";
var Hocsinh = {
    name: "John",
    printName: function(){
        console.log(this.name);
    }
};
//Lệnh gọi đầu tiên
var printHocsinhName = Hocsinh.printName;
printHocsinhName();                          // Peter
//Lệnh gọi thứ 2
Hocsinh.printName();                         // John

Như ta đã thấy, kết quả của 2 lần gọi hàm này cho ta 2 kết quả khác nhau. Tại sao lại như vậy? Thử đưa ra câu trả lời xem nào. Cho bạn 100 giây suy nghĩ: 1 … 2 …3 …4 … 5 … 100 … hết giờ =))

Ở lần gọi đầu tiên, hàm này được kích hoạt không bởi đối tượng cụ thể nào, tức là context của hàm printName được gán là biến toàn cục window, trong khi đó đối tượng gọi hàm ở lần chạy thứ 2 là đối tượng Hocsinh, do vậy mà giá trị của this sẽ có giá thị của đối tượng Hocsinh.

Sự nhập nhằng của giá trị this trong Javascript có thể khiến lập trình viên bối rối, và nếu bạn muốn vượt qua các buổi phỏng vấn cũng như nâng cao kĩ năng viết code Javascript của mình, thì nên đầu tư tìm hiểu khái niệm này kĩ hơn.

Bind(), Call(), Apply()

Đi kèm với các hàm này là vấn đề liên quan tới giá trị của con trỏ “this”, ta xem lại ví dụ ở trên, lí do mà 2 lần gọi hàm ta được 2 kết quả khác nhau là bởi vì context của con trỏ this ở 2 tình huống là khác nhau. Nếu ta muốn kết quả in ra đúng với ý định của mình, thì ta có thể gán context cho con trỏ this một cách tường minh bằng các hàm bind(), call() hoặc apply().

Bây giờ thử trả lời câu hỏi: làm thế nào cả lệnh gọi printHocsinhName cho ta kết quả là “John”, và làm thế nào để ta có thể gọi hàm Hocsinh.printName mà lại có kết quả là “Peter”???

Chúng ta lại có thêm 100s suy nghĩ nữa nào: 1 … 2 … 3 … 99 … 100 … và … hết giờ ^^. Cùng thử so sánh đáp án xem nào:

1
2
3
4
5
6
7
8
9
10
//Cách thứ nhất
var printHocsinhName = Hocsinh.printName.bind(Hocsinh);
printHocsinhName();                                      // John
//Hoặc cách thứ 2
var printHocsinhName = Hocsinh.printName;
printHocsinhName.call(Hocsinh);                          // John
//Xử lí Hocsinh.printName
Hocsinh.printName.call(window);                           //Peter

Bản chất của câu hỏi này nhằm kiểm tra các kiến thức của bạn về khái niệm context của con trỏ this khi được gọi, và các cách thức để kiểm soát được giá trị này một cách chặt chẽ hơn. Cả 3 hàm này đều có ý nghĩa là gán giá trị của con trỏ this khi được chạy tới một giá trị cụ thể nào đó, và nếu bạn nắm được khái niệm context của con trỏ this, bạn sẽ giải quyết được câu hỏi này cách dễ dàng.

Nhắc lại 1 chút, bản thân một hàm (function) cũng là 1 object trong Javascript, vậy nên chúng ta sẽ rất hay bắt gặp các tình huống làm thay đổi context của hàm như: truyền qua object function, hàm mượn, callback, … Nắm được cách sử dụng các hàm bind(), call(), apply() sẽ giúp chúng ta kiểm soát tốt và đảm bảo được kết quả chạy của các hàm đúng với mong muốn.

Ngoài ra, bạn cũng cần coi thêm các kiến thức về cơ chế xử lí bất đồng bộ như: các thao tác gọi xử lí AJAX, các kiểu Promise hỗ trợ thực thi bất đồng bộ và giúp tránh callback hell, v.v.v Nắm được hết các vấn đề này, thì bạn có thể tự tin là mình đã biết được khá rõ về Javascript rồi đó.

TopDev via Nhungdongcodevui

Xem thêm việc làm developer trên TopDev

Migrate từ hệ thống monolithic sang microservice – part 2

Để thực hiện migrate sang microservice, thì phương pháp Domain Driven Design được sử dụng. Tuy nhiên được cải biên thành DDD Incremental Design. Phương pháp này thực chất là SA và Domain expert cùng ngồi lại với nhau để xác định Bounded Context, hiểu đơn giản là bóc tách 1 phần chức năng của hệ thống cũ và tạo ra 1 logical border quanh nó. Thật sự thì đọc giải thích trên Google về các khái niệm domain và bounded context rất khó hiểu. 

Link phần 1 đây nhé các bạn : https://topdev.vn/blog/migrate-tu-he-thong-monolithic-sang-microservice/

  • Từ hệ thống cũ phân tích nghiệp vụ, bóc ra một chức năng business cụ thể (ví dụ như order, payment)
  • Tạo 1 border bao quanh business logic và data đó, những module khác muốn truy cập thì phải qua interface

Khái niệm Big Ball of Mud

Legacy system đang theo kiến trúc monolithic, giống như một quả bóng bùn khổng lồ (gigantic ball of mud), anti-pattern. Business logic được code theo kiểu xâu chuỗi theo kiểu hàm A gọi hàm B, B lại gọi cho C qua rất nhiều level function call. Không hề có một biên giới rõ ràng giữa data và chức năng. Đó là 1 challenging khi migrate sang microservice.

Việc xác định Bounded Context trong hệ thống đòi hỏi cần có Domain Expert, những người hiểu rất sâu sắc business của hệ thống, biết được các module liên hệ qua lại như thế nào, dependency ra sao.

Domain Expert cần mô tả được hệ thống có bao nhiêu module, quan hệ giữa các module như thế nào

Ví dụ minh họa đơn giản

Những module như Order có nhiều quan hệ với module khác thì việc migrate sang microservices sẽ phức tạp hơn nhiều so với các module độc lập như Product. 

Từ các module trên sẽ bóc nhỏ thành các Bounded Context

Build model và interface

Sau khi Bounded Context được xác định, bước tiếp sẽ tạo interface. Phần lớn trường hợp thì interface được thiết kế thành Rest API. Ngoài ra thì có thể sử dụng Queue, event streams.

Các module bên ngoài sẽ được modify để access bounded context thông qua interface. Do đó mỗi khi bóc tách module thành microservice, thì những phần còn lại của hệ thống mono sẽ được sửa đổi để gọi chúng qua interface thay vì function call như trước. Quá trình bóc tách sửa chữa cứ được làm từ từ từng bước một cho đến khi toàn bộ hệ thống migrate xong. Tức là hệ thống mới và cũ vẫn cùng tồn tại (co-exist) cho đến khi hoàn thành migration. Gọi là mô hình Strangler pattern, hiểu nhanh là mô hình dây leo tầm gửi, dây leo (microservice bám quanh) cây chủ (monolithic) cắn từng miếng của cây chủ để lớn dần. cây chủ ngày càng nhỏ dần rồi chết khô

Chốt hạ là sao cho 2 hệ thống mono và microservice có thể chạy đồng thời mà ko ảnh hưởng gì đến business, user sử dụng vẫn ko cảm thấy khác biệt gì, là một challenging thứ 2. Vì quá trình migration sẽ được tính bằng năm.

Ví dụ Order Service được tách ra thành service độc lập từ monolithic ban đầu. Xác định dc bounded context Order từ monolithic tách ra thành Order microservice, phần code đang gọi legacy order qua function call được modified để switch sang gọi bằng API (order API)

Migrate data

Bước cuối cùng là làm sao break down DB monolithic ra thành db riêng own by service. 

Challenging ở đây là làm sao migrate data từ mono db sang microservice db. Vì data là tài sản của tổ chức, không phải của ứng dụng, data lên tới hàng triệu record với hàng trăm bảng quan hệ chằng chịt.

Việc break down và migrate data như thế nào, đón đọc bài sau.

TechTalk via Giaosucan

Code 101: Dành cho ai muốn học lập trình

code-101-danh-cho-ai-muon-hoc-lap-trinh

Càng ngày lập trình trở nên càng phổ biến, cũng như Word và Excel 10–15 năm trước vậy. Để thành công với nghề kỹ sư phần mềm thì cũng cần tố chất phù hợp, nhưng để hiểu và ứng dụng trong cuộc sống thì lập trình cũng không khó hơn Word và Excel. Nếu bạn biết dùng các hàm trong Excel thì có nghĩa là bạn đã lập trình rồi đấy!

Bài viết này giới thiệu một số trò chơi và website dạy tư duy lập trình. Sau đó là các đường dẫn để học Python miễn phí. Cộng đồng VISC có nhiều bạn không làm phần mềm và muốn hiểu hơn về cryptocurrency thì hiểu biết một chút về lập trình sẽ giúp các bạn thoải mái hơn khi đọc các tài liệu kỹ thuật hoặc học các khoá nâng cao.

Lập Trình Là Gì?

Nói một cách đơn giản, lập trình là quá trình viết các lệnh và thao tác để máy tính thực hiện. Nếu bạn có thể chia nhỏ bài toán thành từng bước với các thao tác chi tiết thì bạn có thể bắt đầu lập trình. Không phải lúc nào lập trình cũng là viết ngôn ngữ gần giống tiếng Anh. Những năm 1960 đến những năm 1980, nhiều nơi các nhà khoa học hay nghiên cứu sinh, kỹ sư dùng giấy đục lỗ. Bố tôi cũng vẫn còn giữ một xấp giấy giống thế này:

Ngày nay lập trình đã được bình dân hoá và có vô vàn các ngôn ngữ gần với tiếng Anh thông dụng và thậm chí là xếp các hình khối để diễn tả các bước, các thao thác cho máy tính hiểu:

Lập trình ngày nay đã rất đơn giản và dễ nhập môn hơn ngày xưa rất nhiều. Ở phần sau chúng ta sẽ xem một số trò chơi trong xu hướng gamification để dạy lập trình

Các Trò Chơi Dạy Tư Duy Lập Trình

Trò chơi đầu tiên phải nói đề là Blocky — Do Google và MIT hợp tác sản xuất để dạy tư duy lập trình cho trẻ em và người lớn mới nhập môn:

Blockly | Google Developers
Visual programming editor. Drag and drop blocks to generate executable code. Designed for developers to embed into…developers.google.com

Blocky bắt nguồn từ dự án Scratch của MIT nhưng Scratch thì cần Flash, còn Blocky thì không. Tôi khuyến khích các bạn dùng Blocky vì nó hiện đại hơn.

Trò chơi thứ hai là Human Resource Machine:

Tomorrow Corporation : Human Resource Machine
Tomorrow Corporation is an independent game developer behind indie games Little Inferno and Human Resource Machinetomorrowcorporation.com

Trò chơi rất thú vị và ngộ nghĩnh. Bạn có thể học lập trình trên phone và chơi qua các phần của trò chơi. Cảm ơn bạn Nguyễn Lan Chi đã giới thiệu.

Sau khi làm quen với các trò chơi rèn luyện tư duy lập trình, bạn có thể học một ngôn ngữ lập trình rất hot hiện nay: Python.

Vì Sao Chọn Python?

Ngày nay, số ngôn ngữ lập trình đã lên tới hàng trăm. Theo một thống kê không đầy đủ trên Wikipedia, bạn có thể thấy hơn 700 ngôn ngữ lập trình: https://en.wikipedia.org/wiki/List_of_programming_languages . Theo thống kê của IEEE, Python nằm trong top 10 ngôn ngữ lập trình phổ biến nhất, và năm 2017 Python leo lên vị trí quán quân: https://spectrum.ieee.org/computing/software/the-2017-top-programming-languages

Python cũng rất dễ học và ứng dụng trong nhiều lĩnh vực: Web, desktop, server, công cụ cho quản trị mạng, dạy học, trí tuệ nhân tạo, xử lý ảnh, game, … Bởi vậy tôi chọn Python để giới thiệu với các bạn trong bài viết này.

Các Công Cụ và Tài Liệu Học Python Miễn Phí

Để học một ngôn ngữ lập trình hiệu quả, bạn cần có những công cụ hỗ trợ. Sau đây là trang Learn Python dành cho newbie:

Learn Python – Free Interactive Python Tutorial
LearnPython.org is a free interactive Python tutorial for people who want to learn Python, fast.www.learnpython.org

Bạn có thể chạy trực tiếp trên browser mà chưa phải cài đặt gì. Để hiểu sâu hơn về các vòng lặp, cấu trúc điều kiện, bạn có thể sử dụng công cụ diễn giải Python Tutor:

Python Tutor – Visualize Python, Java, JavaScript, TypeScript, Ruby, C, and C++ code execution
Using this tool, you can write Python 2, Python 3, Java, JavaScript, TypeScript, Ruby, C, and C++ code in your web…pythontutor.com

Sau khi nắm vững các phần cơ bản, bạn có thể tự học các khoá học miễn phí trên Coursera:

Python Courses | Coursera
Learn Python online from 164 Python courses from top institutions like University of Michigan and University of Toronto…www.coursera.org

Hoặc đọc một số tài liệu như Python Crash Course:

Python Crash Course | No Starch Press
“It has been interesting to see, over the last few years, No Starch Press, which produces this book, growing and…www.nostarch.com

Hay là cuốn sách miễn phí “Python rất là cơ bản” dành cho dân lập trình học Python của bạn Võ Duy Tuấn:

Download sách “Python rất là cơ bản”
Hiện nay, Python là một trong những ngôn ngữ lập trình đang được chú ý bởi tính đa dạng về ứng dụng, thư viện phong ph…bloghoctap.com

Tôi cũng rất khuyến khích đọc thêm cuốn “The Hitchhiker’s Guide to Python!” (hoàn toàn miễn phí):

The Hitchhiker’s Guide to Python! – The Hitchhiker’s Guide to Python
This handcrafted guide exists to provide both novice and expert Python developers a best practice handbook to the…docs.python-guide.org

Khi học Python, bạn nên dùng Jupyter Notebook để có thể chạy Python trên browser và sửa code & chạy ngay mà không phải dùng terminal/console nhằm có trải nghiệm tốt nhất. Để cho đơn giản, bạn nên cài đặt luôn trọn gói Anaconda có chứa Jupyter và các thư viện Python phổ biến nhất.

Chúc các bạn học lập trình vui vẻ, tự tin và hiệu quả!

Bài viết của anh Lê Quốc Việt trên medium.com

Muốn phát triển AI cần nuôi dưỡng được niềm đam mê

AI – Trí tuệ nhân tạo xưa và mồi lửa thiên, địa, nhân

AI của thế hệ xưa

Nếu như bạn có cơ hội tiếp xúc với máy tính từ những năm 1990, có lẽ AI chỉ được hiểu như là những đối thủ trong trò chơi điện tử, với những phương thức lập trình vô cùng đơn giản và nhàm chán, nhưng lại là một phần trò cười trong trò chơi đó. Giả dụ như phiên bản Mario trong trò chơi điện tử 4 nút (Nintendo Entertainment System – N.E.S), những con rùa xanh và những con quái vật nâu chỉ biết đi qua đi lại để cố tỏ ra nguy hiểm. Đó chính là bộ mặt điển hình của AI thời điểm đó.
Thế rồi những phim “bom tấn” Hollywood cố tô vẽ một ước mơ, một khát khao cháy bỏng của con người là biến những trò cười trong trò chơi điện tử kia thành những cỗ máy khổng lồ, siêu thông minh, và có phần đáng sợ như trong Ma Trận (Matrix), Kẻ Hủy Diệt (The Terminator). Khi đó, máy tính biết suy nghĩ độc lập, đưa ra quyết định, trợ giúp hoặc trở thành kẻ thù của con người.
Bộ phim khắc họa AI thông minh và hủy diệt.
Trước đây người ta chỉ có thể sử dụng trí tưởng tượng để khắc họa nên những cỗ máy như thế, nhưng nay không thể nói rằng những gì trên phim ảnh là hoàn toàn tưởng tượng được nữa. Những cỗ máy hiện thời đang trở nên vô cùng thông minh một cách đáng kinh ngạc.
Vậy điều gì đã khiến AI trở nên tiến bộ vượt bậc trong thập kỷ qua để tiến hóa từ những con rùa xanh thành những sản phẩm “Powered by AI” với giá trị hàng trăm triệu đô la Mỹ?
Trước khi trả lời câu hỏi đó, hãy nhìn vào một số định nghĩa. AI là một cụm từ mang hàm ý rất rộng, nói đến những cỗ máy hay thuật toán thông minh có thể xử lý các tình huống một cách nhịp nhàng tùy vào hoàn cảnh, chứ không còn là một chương trình được định sẵn để rồi mắc các sai lầm ngớ ngẩn.
Máy học (Machine Learning) là một định nghĩa nhỏ hơn nằm bên trong AI, nói về các thuật toán làm cho máy tính có thể cải thiện được độ chính xác của các công việc theo số lượng dữ liệu đầu vào. Và Học sâu (Deep Learning) là một định nghĩa nhỏ hơn nữa, nằm bên trong Máy học, nói đến một loại thuật toán cụ thể là mạng nơron nhân tạo, một cấu trúc tính toán được phát minh lấy cảm hứng từ cấu trúc não bộ con người.
Sẽ không ngoa khi nói rằng, Máy học là linh hồn của AI, và Học sâu đã đem lại thành công, sự chú ý, cũng như tiềm năng vô tận đến cho Máy học. Sự bùng nổ của các thuật toán Học sâu gần đây đến từ sự cải tiến về phần cứng của máy móc, khi các vi xử lý đồ họa (GPU) giúp tăng tốc tính toán cho các mạng nơron nhân tạo lên gấp hàng ngàn lần so với một lõi vi xử lý trung tâm (CPU). Ngoài ra, một nguyên nhân không thể thiếu đó là nguyên liệu cho các thuật toán Học sâu hoạt động chính là lượng dữ liệu chúng ta tạo ra trong những năm gần đây đã vượt xa kỳ vọng, với 90% tổng lượng dữ liệu là chỉ mới được tạo ra trong vỏn vẹn hai năm gần đây.
Thiên thời (dữ liệu lớn), địa lợi (sức mạnh tính toán cao), và nhân hòa (các tài năng trong lĩnh vực AI) chính là mồi lửa tạo nên cuộc cách mạng khiến cho các phát minh AI đang dần trở nên vô cùng quan trọng và len lỏi vào cuộc sống của con người, cũng giống như dòng điện mà Benjamin Franklin đã tìm ra vào ba thế kỷ trước đây. Vậy thì cuộc sống chúng ta sẽ thay đổi ra sao khi đồng hành cùng với AI?

Xử lý ngôn ngữ tự nhiên (Natural Language Processing)

AI đã và đang đọc hàng triệu, hàng tỉ văn bản mỗi ngày, tổng hợp và thậm chí là tóm tắt lại những kiến thức trong mớ tài liệu khổng lồ mà không một cá thể loài người nào có thể làm được. AI giúp con người trả lời câu hỏi sau khi đọc các kiến thức đó, đưa ra gợi ý, hoàn thành câu viết, và có khi là viết hộ luôn con người. Gần đây, những nhà nghiên cứu của viện Open AI, nơi mà tỉ phú Elon Musk đầu tư mạnh, đã đưa ra một mô hình AI có khả năng tạo ra văn bản với độ “thật” đến đáng sợ, khiến cho họ quyết định không xuất bản toàn bộ thuật toán vì lo sợ nguy cơ bị lạm dụng. Điều này khiến cho cả thế giới khoa học giật mình vì nó quan trọng đến mức tổ chức Open AI đã phải đi ngược lại với chữ “Open” (Mở) của họ.

Thị giác máy tính (computer vision)

Và thị giác máy tính (computer vision) là ngành được hưởng lợi nhiều nhất trong cuộc cách mạng AI. Hình ảnh là thứ con người tự hào đem lại rất nhiều thông tin mà nay đã được máy tính học và dần dần thắng thế. Từ những ứng dụng bảo mật như nhận dạng khuôn mặt, đến các thuật toán tìm ra những hình ảnh, thước phim nhạy cảm, các công ty công nghệ lớn như Apple, Facebook, hay Google đều coi AI là trái tim trong các sản phẩm công nghệ của họ.
Đơn giản rằng, nếu thiếu đi AI, các sản phẩm công nghệ của họ không thể nào phát triển (scale) đến hàng tỉ người dùng được. Và tuyệt vời thay, đến các ngành như y tế cũng đã ứng dụng công nghệ AI vào việc giúp các bác sĩ chẩn đoán các bệnh như lao, ung thư, khối u một cách chính xác hơn ngày xưa rất nhiều.

Từ Mỹ đến Nhật

Mỹ là đầu tàu của cả thế giới về lĩnh vực AI bởi vì Mỹ là cái nôi của Internet. Thành công của các công ty có công nghệ AI bậc nhất thế giới như Facebook, Google, hay Amazon đến từ lượng dữ liệu khổng lồ họ có được khi là một công ty Internet. Đây là điểm mấu chốt để thành công, bên cạnh những sự đầu tư khổng lồ về cơ sở hạ tầng tính toán và nhân tài trong lĩnh vực này.
Vậy đâu là cánh cửa cho các công ty khác, các đất nước khác muốn phát triển AI và cạnh tranh với các ông lớn này?
Làm việc tại Nhật Bản trong những năm qua cho tôi thấy cách người Nhật tạo ra lợi thế cạnh tranh của họ. Khó có đất nước nào tạo được nhiều dữ liệu về quá trình sản xuất tiên tiến và cơ sở hạ tầng thông minh như Nhật Bản. Họ lấy đó làm tiền đề để thúc đẩy ngành AI cho nước Nhật và cạnh tranh với những việc mà Facebook, hay Google không thể nào động đến họ được. Khi nghiên cứu ở Nhật, tôi đã phát minh ra thuật toán AI có thể tự động hóa quy trình lấy và xử lý nước trong một nhà máy lọc nước vô cùng hiện đại với các cảm ứng được lắp đặt ở khắp nơi.

Và đến Việt Nam

Nhìn về Việt Nam, đâu là cơ hội cho chúng ta trở thành một nước cạnh tranh về AI trên bản đồ thế giới. Giống như ở trên tôi phân tích, ba yếu tố quan trọng đó là thiên thời (dữ liệu lớn), địa lợi (sức mạnh tính toán cao), và nhân hòa (các tài năng trong lĩnh vực AI) cần phải được cân nhắc để tạo nên tính khác biệt với các nước trên thế giới.

Thứ nhất, thiên thời – dữ liệu của người Việt.

Việt Nam có số lượng người sử dụng điện thoại thông minh rất cao (lên đến 72% tỷ lệ thâm nhập theo báo cáo của Appota), một phương tiện để thu thập cũng như sản sinh ra dữ liệu rất lớn. Ngoài ra, những bộ dữ liệu “độc quyền” của người Việt có thể kể đến ngôn ngữ, văn bản, hình ảnh trong nước, hay các ngành nghề rất Việt Nam.
Việt Nam cần có một cơ sở dữ liệu của riêng mình, và tốt nhất là được mở ra cho các nhà nghiên cứu về AI có thể truy cập và sử dụng một cách an toàn. Nếu chúng ta không làm được điều này ở cấp độ nhà nước, thì chỉ còn trông đợi vào các doanh nghiệp tư nhân đua nhau thu thập dữ liệu và tạo ra thế mạnh cho riêng họ. Nhưng liệu các ông chủ tư nhân có đủ thoải mái để kết hợp với nhau, chia sẻ tài nguyên với nhau, để cùng cạnh tranh với thế giới hay không?

Thứ hai, địa lợi – cơ sở hạ tầng tính toán

Một trong các điều kiện tiên quyết để bứt phá trong ngành AI đó là sức mạnh tính toán. Các trường đại học thành công trong nghiên cứu về AI đều có cơ sở hạ tầng tính toán lớn, đủ sức để đưa ra kết quả của một thí nghiệm có quy mô lớn trong… vài ngày. Việc liên tục có được những kết quả thí nghiệm thúc đẩy vòng lặp quá trình tìm hiểu, sửa lỗi, nâng cấp diễn ra nhanh hơn.
Ở Việt Nam, ngoài một số công ty lớn về công nghệ đang nỗ lực đầu tư cơ sở hạ tầng tính toán (tuy nhiên không rõ có phải để dành cho nghiên cứu hay cho việc làm sản phẩm), thì cơ sở hạ tầng tính toán ở các trường đại học ở Việt Nam gần như không có sức cạnh tranh.

Cuối dùng, nhân hòa – tài năng trong lĩnh vực AI

Nhìn vào thành công của người Việt trên thế giới, có lẽ không quá tự kiêu khi nói rằng người Việt rất phù hợp với làm AI.
Chúng ta có những người dẫn đầu thế giới trong ngành và có những người có tầm ảnh hướng lớn trong ngành. Tuy nhiên, nên nhớ rằng họ đều được đào tạo bài bản ở môi trường giáo dục nâng cao (đại học và sau đại học) tại các nước phát triển. Đồng thời, chúng ta có một chương trình toán cơ bản (bậc dưới đại học) khá tốt.
Vậy vì đâu chúng ta lại vẫn thiếu nhân lực cao cấp trong ngành đến như vậy? Theo tôi, từ bậc đại học trở lên, còn quá ít những người có niềm đam mê với nghiên cứu cơ bản về toán, về ngành khoa học máy tính, và về AI nói riêng. Có phải chúng ta chưa nuôi dưỡng được niềm đam mê đó? Bản thân tôi, cũng bắt đầu làm việc với các học sinh cấp 3 về ngành AI để nuôi dưỡng những tài năng và đam mê cho AI Việt sau này. Nếu như không ai quan tâm, chúng ta không thể có nguồn lực dồi dào, chất lượng cho những dự án tham vọng về AI của đất nước sau này.
Phong và giáo sư Yoshua Bengio (giám đốc MILA)
Tôi từng gặp Giáo sư Yoshua Bengio, một trong những cha đẻ của ngành AI tiên tiến thời nay, tại Montreal, Canada. Ông có nói với tôi rằng, họ, những sinh viên đang nghi ngờ những điều ông giảng, chính là những người sẽ thay đổi thế giới sau này, chứ không phải là ông nữa. Đó là điều khiến tôi mong muốn được làm việc với ông trong tương lai, và đương nhiên bản thân tôi cũng mong muốn được biết thêm nhiều bạn trẻ Việt Nam như vậy.
TopDev via Giaosucan

Hướng dẫn giải bài toán phân bổ số lượng (thuật toán chia kẹo) trong sqlserver

Xin chào các bạn, bài viết hôm nay mình sẽ hướng dẫn các bạn cách giải bài toán phân bổ số lượng (bài toán chia kẹo) trong lập trình cơ sở dữ liệu sqlserver.

Xin chào các bạn, bài viết hôm nay mình sẽ hướng dẫn các bạn cách giải bài toán phân bổ số lượng (bài toán chia kẹo) trong lập trình cơ sở dữ liệu sqlserver.

Ví dụ: mình có hình database như ở bên dưới

candy_algorithm_sqlserver

Ở hình trên các bạn thấy:

+ Mình có số lượng của tổng PO là : 375 [DocEntry = 1](số lượng vào)

+ Và một bản hiển thị ở dưới cần chia cho [DocEntry] lần lượt là: 100, 200 và 200.

Nếu với số lượng như hình trên kết quả mình muốn hiển thị như hình dưới lần lượt là: 100, 200, sau khi chia hết 300 thì còn 75 chia cho column thứ 3.

Nếu số lượng vào dư lên, sẽ được cộng dồn vào cho column ở ô cuối cùng.

Kết quả khi chạy truy vấn SQL chia kẹo:

candy_sqlserver

Câu lệnh truy vấn T-SQL:

DROP TABLE #po;
DROP TABLE #inv;
DROP TABLE #temp;
CREATE TABLE #PO
(
    PO VARCHAR(10),
    DocEntry INT,
    Qty INT
);
CREATE TABLE #INV
(
    INV VARCHAR(10),
    DocEntry INT,
    Qty INT
);
INSERT INTO #PO
(
    PO,
    DocEntry,
    Qty
)
VALUES
('A123', 1, 200);
INSERT INTO #PO
(
    PO,
    DocEntry,
    Qty
)
VALUES
('A123', 1, 150);
INSERT INTO #PO
(
    PO,
    DocEntry,
    Qty
)
VALUES
('A123', 1, 25);
INSERT INTO #PO
(
    PO,
    DocEntry,
    Qty
)
VALUES
('B123', 2, 50);
INSERT INTO #PO
(
    PO,
    DocEntry,
    Qty
)
VALUES
('B123', 2, 30);
INSERT INTO #INV
(
    INV,
    DocEntry,
    Qty
)
VALUES
('IV100', 1, 100);
INSERT INTO #INV
(
    INV,
    DocEntry,
    Qty
)
VALUES
('IV102', 2, 200);
INSERT INTO #INV
(
    INV,
    DocEntry,
    Qty
)
VALUES
('IV105', 1, 200);
INSERT INTO #INV
(
    INV,
    DocEntry,
    Qty
)
VALUES
('IV106', 2, 190);
INSERT INTO #INV
(
    INV,
    DocEntry,
    Qty
)
VALUES
('IV112', 1, 200);

CREATE TABLE #temp
(
    PO VARCHAR(10),
    DocEntry INT,
    Qty INT
);
INSERT INTO #temp
(
    PO,
    DocEntry,
    Qty
)
SELECT PO,
       DocEntry,
       SUM(Qty)
FROM #PO
GROUP BY PO,
         DocEntry;
SELECT a.PO,
       a.Qty,
       a.Quantity,
       DevideCandy = CASE
                         WHEN a.conlai2 > 0
                              AND a.conlai2 >= a.Quantity THEN
                             a.Quantity
                         WHEN a.conlai2 > 0
                              AND a.conlai2 < a.Quantity THEN
                             a.Quantity
                         WHEN (a.conlai2 + a.Quantity) < 0 THEN
                             0
                         ELSE
                             a.conlai2 + a.Quantity
                     END
FROM
(
    SELECT p.DocEntry,
           p.PO,
           p.Qty,
           i.INV,
           i.Qty AS Quantity,
           conlai = IIF(
                        p.Qty -
                        (
                            SELECT SUM(rt.Qty)
                            FROM #INV rt
                            WHERE (rt.DocEntry = i.DocEntry)
                                  AND (rt.INV <= i.INV)
                        ) < 0,
                        0,
                        p.Qty -
                        (
                            SELECT SUM(rt.Qty)
                            FROM #INV rt
                            WHERE (rt.DocEntry = i.DocEntry)
                                  AND (rt.INV <= i.INV)
                        )),
           conlai2 = p.Qty -
                     (
                         SELECT SUM(rt.Qty)
                         FROM #INV rt
                         WHERE (rt.DocEntry = i.DocEntry)
                               AND (rt.INV <= i.INV)
                     )
    FROM #temp p
        LEFT JOIN #INV i
            ON (p.DocEntry = i.DocEntry)
) a
ORDER BY a.DocEntry,
         a.INV;

Các bạn có thể copy đoạn script này về để test thử.

Có thể bạn quan tâm:

Xem thêm việc làm SQL Server trên TopDev

TopDev via Laptrinhvb

Bỏ túi Cheatsheet React cho năm 2023 (kèm ví dụ thực tế)

Bỏ túi Cheatsheet React cho năm 2020 (kèm ví dụ thực tế)

Nhắc đến Cheatsheet thì bài viết này không hẳn chỉ là bản tóm tắt những tính năng của React. Trong quá trình làm việc với React, tôi đã đúc kết một số kiến thức thực tế và pattern từ kinh nghiệm bản thân. Mỗi phần trong bài viết này sẽ hướng dẫn chi tiết về React cùng những ví dụ thực tế.

CORE CONCEPTS

Các element và JSX

  • Cú pháp căn bản cho một React element:
// In a nutshell, JSX allows us to write HTML in our JS
// JSX can use any valid html tags (i.e. div/span, h1-h6, form/input, etc)
<div>Hello React</div>
  • Cú pháp JSX là expressions
// as an expression, JSX can be assigned to variables...
const greeting = <div>Hello React</div>;

const isNewToReact = true;

// ... or can be displayed conditionally
function sayGreeting() {
  if (isNewToReact) {
    // ... or returned from functions, etc.
    return greeting; // displays: Hello React
  } else {
    return <div>Hi again, React</div>;
  }
}
  • JSX cho phép nest expressions
const year = 2020;
// we can insert primitive JS values in curly braces: {}
const greeting = <div>Hello React in {year}</div>;
// trying to insert objects will result in an error
  • JSX cho phép nest các elements:
// to write JSX on multiple lines, wrap in parentheses: ()
const greeting = (
  // div is the parent element
  <div>
    {/* h1 and p are child elements */}
    <h1>Hello!</h1>
    <p>Welcome to React</p>
  </div>
);
// 'parents' and 'children' are how we describe JSX elements in relation
// to one another, like we would talk about HTML elements
  • HTML và JSX có sự khác biệt một ít về cú pháp
// Empty div is not <div></div> (HTML), but <div/> (JSX)
<div/>

// A single tag element like input is not <input> (HTML), but <input/> (JSX)
<input name="email" />

// Attributes are written in camelcase for JSX (like JS variables
<button className="submit-button">Submit</button> // not 'class' (HTML)
  • App React cơ bản nhất yêu cầu 3 thứ
  • ReactDOM.render() để render app
  • Một JSX element (được gọi là root node)
  • Một DOM element để mount app (thường là một div với một id của root trong index.html file)
// imports needed if using NPM package; not if from CDN links
import React from "react";
import ReactDOM from "react-dom";

const greeting = <h1>Hello React</h1>;

// ReactDOM.render(root node, mounting point)
ReactDOM.render(greeting, document.getElementById("root"));

Component và Prop của React

  • Cấu trúc cho một component React cơ bản
import React from "react";

// 1st component type: function component
function Header() {
  // function components must be capitalized unlike normal JS functions
  // note the capitalized name here: 'Header'
  return <h1>Hello React</h1>;
}

// function components with arrow functions are also valid
const Header = () => <h1>Hello React</h1>;

// 2nd component type: class component
// (classes are another type of function)
class Header extends React.Component {
  // class components have more boilerplate (with extends and render method)
  render() {
    return <h1>Hello React</h1>;
  }
}
  • Cách sử dụng components
// do we call these function components like normal functions?

// No, to execute them and display the JSX they return...
const Header = () => <h1>Hello React</h1>;

// ...we use them as 'custom' JSX elements
ReactDOM.render(<Header />, document.getElementById("root"));
// renders: <h1>Hello React</h1>
  • Component có thể sử dụng lại trong app
// for example, this Header component can be reused in any app page

// this component shown for the '/' route
function IndexPage() {
  return (
    <div>
      <Header />
      <Hero />
      <Footer />
    </div>
  );
}

// shown for the '/about' route
function AboutPage() {
  return (
    <div>
      <Header />
      <About />
      <Testimonials />
      <Footer />
    </div>
  );
}
  • Data có thể chuyển đến component với prop
// What if we want to pass data to our component from a parent?
// I.e. to pass a user's name to display in our Header?

const username = "John";

// we add custom 'attributes' called props
ReactDOM.render(
  <Header username={username} />,
  document.getElementById("root")
);
// we called this prop 'username', but can use any valid JS identifier

// props is the object that every component receives as an argument
function Header(props) {
  // the props we make on the component (i.e. username)
  // become properties on the props object
  return <h1>Hello {props.username}</h1>;
}
  • Props không thể thay đổi được (mutated)
// Components must ideally be 'pure' functions.
// That is, for every input, we be able to expect the same output

// we cannot do the following with props:
function Header(props) {
  // we cannot mutate the props object, we can only read from it
  props.username = "Doug";

  return <h1>Hello {props.username}</h1>;
}
// But what if we want to modify a prop value that comes in?
// That's where we would use state (see the useState section)
  • Chúng ta có thể dùng children prop khi muốn chuyển element/component như là props đến các component khác
// Can we accept React elements (or components) as props?
// Yes, through a special property on the props object called 'children'

function Layout(props) {
  return <div className="container">{props.children}</div>;
}

// The children prop is very useful for when you want the same
// component (such as a Layout component) to wrap all other components:
function IndexPage() {
  return (
    <Layout>
      <Header />
      <Hero />
      <Footer />
    </Layout>
  );
}

// different page, but uses same Layout component (thanks to children prop)
function AboutPage() {
  return (
    <Layout>
      <About />
      <Footer />
    </Layout>
  );
}
  • Hiển thị có điều kiện các components với ternaries và short-circuiting
// if-statements are fine to conditionally show , however...
// ...only ternaries (seen below) allow us to insert these conditionals
// in JSX, however
function Header() {
  const isAuthenticated = checkAuth();

  return (
    <nav>
      <Logo />
      {/* if isAuth is true, show AuthLinks. If false, Login  */}
      {isAuthenticated ? <AuthLinks /> : <Login />}
      {/* if isAuth is true, show Greeting. If false, nothing. */}
      {isAuthenticated && <Greeting />}
    </nav>
  );
}
  • Fragment là các components được dùng để hiển thị các loại component mà không cần thêm element vào DOM.
  • Fragments phù hợp với conditional logic
// we can improve the logic in the previous example
// if isAuthenticated is true, how do we display both AuthLinks and Greeting?
function Header() {
  const isAuthenticated = checkAuth();

  return (
    <nav>
      <Logo />
      {/* we can render both components with a fragment */}
      {/* fragments are very concise: <> </> */}
      {isAuthenticated ? (
        <>
          <AuthLinks />
          <Greeting />
        </>
      ) : (
        <Login />
      )}
    </nav>
  );
}
  5 dự án React buộc phải có trong porfolio của bạn
  React: Thử làm ứng dụng đổi tỷ giá

List và key

Dùng .map() để chuyển mảng list of data  vào list of elements:

const people = ["John", "Bob", "Fred"];
const peopleList = people.map(person => <p>{person}</p>);

.map() cũng được dùng cho components như là elements:

function App() {
  const people = ['John', 'Bob', 'Fred'];
  // can interpolate returned list of elements in {}
  return (
    <ul>
      {/* we're passing each array element as props */}
      {people.map(person => <Person name={person} />}
    </ul>
  );
}

function Person({ name }) {
  // gets 'name' prop using object destructuring
  return <p>this person's name is: {name}</p>;
}

Mỗi React element được lặp lại cần một ‘key’ props. Keys là nhân tố cần thiết để React theo dõi từng element được lặp lại với map.

Nếu không có Keys thì sẽ khó để update các element khi data thay đổi. Và Keys phải là các giá trị duy nhất để thể hiện rằng các elements này tách biệt với nhau.

function App() {
  const people = ['John', 'Bob', 'Fred'];

  return (
    <ul>
      {/* keys need to be primitive values, ideally a generated id */}
      {people.map(person => <Person key={person} name={person} />)}
    </ul>
  );
}

// If you don't have ids with your set of data or unique primitive values,
// you can use the second parameter of .map() to get each elements index
function App() {
  const people = ['John', 'Bob', 'Fred'];

  return (
    <ul>
      {/* use array element index for key */}
      {people.map((person, i) => <Person key={i} name={person} />)}
    </ul>
  );
}

Events và Event Handlers

Events trong React và HTML có sự khác biệt nhỏ

// Note: most event handler functions start with 'handle'
function handleToggleTheme() {
  // code to toggle app theme
}

// in html, onclick is all lowercase
<button onclick="handleToggleTheme()">
  Submit
</button>

// in JSX, onClick is camelcase, like attributes / props
// we also pass a reference to the function with curly braces
<button onClick={handleToggleTheme}>
  Submit
</button>

Hai thứ quan trọng nhất trong React events là onClick và onChange

  • onClick xử lý click events trên JSX element (được gọi là buttons)
  • onChange thì xử lý keyboard events (được gọi là inputs)
function App() {
  function handleChange(event) {
    // when passing the function to an event handler, like onChange
    // we get access to data about the event (an object)
    const inputText = event.target.value;
    const inputName = event.target.name; // myInput
    // we get the text typed in and other data from event.target
  }

  function handleSubmit() {
    // on click doesn't usually need event data
  }

  return (
    <div>
      <input type="text" name="myInput" onChange={handleChange} />
      <button onClick={handleSubmit}>Submit</button>
    </div>
  );
}

React Hooks

State và useState

useState cho phép chúng ta khai báo local state trong Function Component:

import React from 'react';

// create state variable
// syntax: const [stateVariable] = React.useState(defaultValue);
function App() {
  const [language] = React.useState('javascript');
  // we use array destructuring to declare state variable

  return <div>I am learning {language}</div>;
}

Ghi chú: Bất kỳ hook trong phần này được lấy từ React packafe và có thể import riêng lẻ.

import React, { useState } from "react";

function App() {
  const [language] = useState("javascript");

  return <div>I am learning {language}</div>;
}

useState cũng cho chúng ta chức năng ‘setter’ để update các state mà nó tạo

function App() {
  // the setter function is always the second destructured value
  const [language, setLanguage] = React.useState("python");
  // the convention for the setter name is 'setStateVariable'

  return (
    <div>
      {/*  why use an arrow function here instead onClick={setterFn()} ? */}
      <button onClick={() => setLanguage("javascript")}>
        Change language to JS
      </button>
      {/*  if not, setLanguage would be called immediately and not on click */}
      <p>I am now learning {language}</p>
    </div>
  );
}

// note that whenever the setter function is called, the state updates,
// and the App component re-renders to display the new state

useState có thể sử dụng một hoặc nhiều lần với single component:

function App() {
  const [language, setLanguage] = React.useState("python");
  const [yearsExperience, setYearsExperience] = React.useState(0);

  return (
    <div>
      <button onClick={() => setLanguage("javascript")}>
        Change language to JS
      </button>
      <input
        type="number"
        value={yearsExperience}
        onChange={event => setYearsExperience(event.target.value)}
      />
      <p>I am now learning {language}</p>
      <p>I have {yearsExperience} years of experience</p>
    </div>
  );
}

useState chấp nhận các value primitive hay object để quản lý state:

// we have the option to organize state using whatever is the
// most appropriate data type, according to the data we're tracking
function App() {
  const [developer, setDeveloper] = React.useState({
    language: "",
    yearsExperience: 0
  });

  function handleChangeYearsExperience(event) {
    const years = event.target.value;
    // we must pass in the previous state object we had with the spread operator
    setDeveloper({ ...developer, yearsExperience: years });
  }

  return (
    <div>
      {/* no need to get prev state here; we are replacing the entire object */}
      <button
        onClick={() =>
          setDeveloper({
            language: "javascript",
            yearsExperience: 0
          })
        }
      >
        Change language to JS
      </button>
      {/* we can also pass a reference to the function */}
      <input
        type="number"
        value={developer.yearsExperience}
        onChange={handleChangeYearsExperience}
      />
      <p>I am now learning {developer.language}</p>
      <p>I have {developer.yearsExperience} years of experience</p>
    </div>
  );
}

Nếu state mới phụ thuộc vào state trước đó, để đảm bảo update đã hoàn tất, chúng ta có thể dùng một function trong setter function đưa ra chính xác state trước đó.

function App() {
  const [developer, setDeveloper] = React.useState({
    language: "",
    yearsExperience: 0,
    isEmployed: false
  });

  function handleToggleEmployment(event) {
    // we get the previous state variable's value in the parameters
    // we can name 'prevState' however we like
    setDeveloper(prevState => {
      return { ...prevState, isEmployed: !prevState.isEmployed };
      // it is essential to return the new state from this function
    });
  }

  return (
    <button onClick={handleToggleEmployment}>Toggle Employment Status</button>
  );
}

Side effects và useEffect

useEffect cho phép thực hiện side effect bên trong các function component. Vậy các side effect là những gì?

  • Sử dụng side effects khi chúng ta cần “đụng” đến thế giới bên ngoài. Ví dụ như fetching data từ API hay làm việc với DOM.
  • Side effect là các hành động có thể thay đổi component state.

useEffect chấp nhận callback function (hay còn được gọi là ‘effect’ function), được chạy mặc định mỗi lần re-render. Nó chạy một lần khi component mount, cũng là thời điểm phù hợp để thực hiện side effect trong một lifecycle của component.

// what does our code do? Picks a color from the colors array
// and makes it the background color
function App() {
  const [colorIndex, setColorIndex] = React.useState(0);
  const colors = ["blue", "green", "red", "orange"];

  // we are performing a 'side effect' since we are working with an API
  // we are working with the DOM, a browser API outside of React
  useEffect(() => {
    document.body.style.backgroundColor = colors[colorIndex];
  });
  // whenever state is updated, App re-renders and useEffect runs

  function handleChangeIndex() {
    const next = colorIndex + 1 === colors.length ? 0 : colorIndex + 1;
    setColorIndex(next);
  }

  return <button onClick={handleChangeIndex}>Change background color</button>;
}

Để tránh thực hiện callback sau mỗi lần render, chúng ta có thể tạo arguement thứ 2 là một array (mảng) trống:

function App() {
  ...
  // now our button doesn't work no matter how many times we click it...
  useEffect(() => {
    document.body.style.backgroundColor = colors[colorIndex];
  }, []);
  // the background color is only set once, upon mount

  // how do we not have the effect function run for every state update...
  // but still have it work whenever the button is clicked?

  return (
    <button onClick={handleChangeIndex}>
      Change background color
    </button>
  );
}

useEffect cho phép chúng ta thực hiện các effect với các mảng dependencies.

Mảng dependencies là arguement thứ hai, nếu có một trong những giá trị trong mảng thay đổi, thì effect function sẽ chạy lại.

function App() {
  const [colorIndex, setColorIndex] = React.useState(0);
  const colors = ["blue", "green", "red", "orange"];

  // we add colorIndex to our dependencies array
  // when colorIndex changes, useEffect will execute the effect fn again
  useEffect(() => {
    document.body.style.backgroundColor = colors[colorIndex];
    // when we use useEffect, we must think about what state values
    // we want our side effect to sync with
  }, [colorIndex]);

  function handleChangeIndex() {
    const next = colorIndex + 1 === colors.length ? 0 : colorIndex + 1;
    setColorIndex(next);
  }

  return <button onClick={handleChangeIndex}>Change background color</button>;
}

useEffect cho phép chúng ta unsubscribe những effect nhất định bằng cách trả lại function vào lúc cuối:

function MouseTracker() {
  const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });

  React.useEffect(() => {
    // .addEventListener() sets up an active listener...
    window.addEventListener("mousemove", event => {
      const { pageX, pageY } = event;
      setMousePosition({ x: pageX, y: pageY });
    });

    // ...so when we navigate away from this page, it needs to be
    // removed to stop listening. Otherwise, it will try to set
    // state in a component that doesn't exist (causing an error)

    // We unsubscribe any subscriptions / listeners w/ this 'cleanup function'
    return () => {
      window.removeEventListener("mousemove", event => {
        const { pageX, pageY } = event;
        setMousePosition({ x: pageX, y: pageY });
      });
    };
  }, []);

  return (
    <div>
      <h1>The current mouse position is:</h1>
      <p>
        X: {mousePosition.x}, Y: {mousePosition.y}
      </p>
    </div>
  );
}

// Note: we could extract the reused logic in the callbacks to
// their own function, but I believe this is more readable

Fetching data với useEffect

Nên chú ý khi xử lý các promise với các cú pháp async/await ngắn gọn yêu cầu phải tạo thêm function riêng biệt (Vì effect callback function không thể async.)

const endpoint = "https://api.github.com/users/codeartistryio";

// with promises:
function App() {
  const [user, setUser] = React.useState(null);

  React.useEffect(() => {
    // promises work in callback
    fetch(endpoint)
      .then(response => response.json())
      .then(data => setUser(data));
  }, []);
}

// with async / await syntax for promise:
function App() {
  const [user, setUser] = React.useState(null);
  // cannot make useEffect callback function async
  React.useEffect(() => {
    getUser();
  }, []);

  // instead, use async / await in separate function, then call
  // function back in useEffect
  async function getUser() {
    const response = await fetch("https://api.github.com/codeartistryio");
    const data = await response.json();
    setUser(data);
  }
}

Performance và useCallback

useCallback là một hook được sử dụng để cải thiện performance của component.

Nếu có một component thường xuyên re-render, useCallback sẽ ngăn chặn tình trạng callback functions trong component được tạo lại mỗi lần component re-render (đồng nghĩa với việc function component re-run).

useCallback re-run chỉ khi một trong những dependencies thay đổi.

// in Timer, we are calculating the date and putting it in state a lot
// this results in a re-render for every state update

// we had a function handleIncrementCount to increment the state 'count'...
function Timer() {
  const [time, setTime] = React.useState();
  const [count, setCount] = React.useState(0);

  // ... but unless we wrap it in useCallback, the function is
  // recreated for every single re-render (bad performance hit)
  // useCallback hook returns a callback that isn't recreated every time
  const inc = React.useCallback(
    function handleIncrementCount() {
      setCount(prevCount => prevCount + 1);
    },
    // useCallback accepts a second arg of a dependencies array like useEffect
    // useCallback will only run if any dependency changes (here it's 'setCount')
    [setCount]
  );

  React.useEffect(() => {
    const timeout = setTimeout(() => {
      const currentTime = JSON.stringify(new Date(Date.now()));
      setTime(currentTime);
    }, 300);

    return () => {
      clearTimeout(timeout);
    };
  }, [time]);

  return (
    <div>
      <p>The current time is: {time}</p>
      <p>Count: {count}</p>
      <button onClick={inc}>+</button>
    </div>
  );
}

Memorization và useMemo

useMemo khá tương tự với useCallback và để cải thiện hiệu năng. Thay vì dùng để callback, nó được dùng để lưu lại kết quả của hàm nào và những giá trị nào sẽ làm thay đổi kết quả đó.

// useMemo is useful when we need a lot of computing resources
// to perform an operation, but don't want to repeat it on each re-render

function App() {
  // state to select a word in 'words' array below
  const [wordIndex, setWordIndex] = useState(0);
  // state for counter
  const [count, setCount] = useState(0);

  // words we'll use to calculate letter count
  const words = ["i", "am", "learning", "react"];
  const word = words[wordIndex];

  function getLetterCount(word) {
    // we mimic expensive calculation with a very long (unnecessary) loop
    let i = 0;
    while (i < 1000000) i++;
    return word.length;
  }

  // Memoize expensive function to return previous value if input was the same
  // only perform calculation if new word without a cached value
  const letterCount = React.useMemo(() => getLetterCount(word), [word]);

  // if calculation was done without useMemo, like so:

  // const letterCount = getLetterCount(word);

  // there would be a delay in updating the counter
  // we would have to wait for the expensive function to finish

  function handleChangeIndex() {
    // flip from one word in the array to the next
    const next = wordIndex + 1 === words.length ? 0 : wordIndex + 1;
    setWordIndex(next);
  }

  return (
    <div>
      <p>
        {word} has {letterCount} letters
      </p>
      <button onClick={handleChangeIndex}>Next word</button>
      <p>Counter: {count}</p>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  );
}

Refs và useRef

Ref là attribute đặc biệt và luôn có sẵn trên tất cả React components. Chúng cho phép chúng ta tạo ra reference đến element/component có sẵn khi mount component.

useRef cho phép chúng ta sử dụng React Refs một cách dễ dàng. Chúng ta gọi useRef (ở top của component đó) và đính kèm giá trị trả về vào attribute ref của element đó để tham chiếu.

Khi đã tạo reference, chúng ta sử dụng property hiện có để chỉnh sửa (modify-mutate) các properties của element đó. Hoặc chúng ta có thể call bất kỳ methods nào trên element đó (như .focus() để focus một input).

function App() {
  const [query, setQuery] = React.useState("react hooks");
  // we can pass useRef a default value
  // we don't need it here, so we pass in null to ref an empty object
  const searchInput = useRef(null);

  function handleClearSearch() {
    // current references the text input once App mounts
    searchInput.current.value = "";
    // useRef can store basically any value in its .current property
    searchInput.current.focus();
  }

  return (
    <form>
      <input
        type="text"
        onChange={event => setQuery(event.target.value)}
        ref={searchInput}
      />
      <button type="submit">Search</button>
      <button type="button" onClick={handleClearSearch}>
        Clear
      </button>
    </form>
  );
}

Hooks nâng cao

Context và useContext

Trong React, chúng ta nên tránh tạo ra các multiple props để chuyển data xuống 2 hay nhiều level từ một parent component:

// Context helps us avoid creating multiple duplicate props
// This pattern is also called props drilling:
function App() {
  // we want to pass user data down to Header
  const [user] = React.useState({ name: "Fred" });

  return (
   {/* first 'user' prop */}
    <Main user={user} />
  );
}

const Main = ({ user }) => (
  <>
    {/* second 'user' prop */}
    <Header user={user} />
    <div>Main app content...</div>
  </>
);

const Header = ({ user }) => <header>Welcome, {user.name}!</header>;

Context khá hữu ích dùng để chuyển props xuống các tầng levels của child 

// Here is the previous example rewritten with Context
// First we create context, where we can pass in default values
const UserContext = React.createContext();
// we call this 'UserContext' because that's what data we're passing down

function App() {
  // we want to pass user data down to Header
  const [user] = React.useState({ name: "Fred" });

  return (
    {/* we wrap the parent component with the provider property */}
    {/* we pass data down the computer tree w/ value prop */}
    <UserContext.Provider value={user}>
      <Main />
    </UserContext.Provider>
  );
}

const Main = () => (
  <>
    <Header />
    <div>Main app content...</div>
  </>
);

// we can remove the two 'user' props, we can just use consumer
// to consume the data where we need it
const Header = () => (
  {/* we use this pattern called render props to get access to the data*/}
  <UserContext.Consumer>
    {user => <header>Welcome, {user.name}!</header>}
  </UserContext.Consumer>
);

useContect hook có thể xóa render props pattern này, tuy nhiên, để có thể consume context trong bất kỳ function component nào:

const Header = () => {
  // we pass in the entire context object to consume it
  const user = React.useContext(UserContext);
  // and we can remove the Consumer tags
  return <header>Welcome, {user.name}!</header>;
};

Reducers và useReducer

Reducer là function khá đơn giản, được dùng để lấy state object trước đó và một action object và trả về một state object mới. Ví dụ:

// let's say this reducer manages user state in our app:
function reducer(state, action) {
  // reducers often use a switch statement to update state
  // in one way or another based on the action's type property
  switch (action.type) {
    // if action.type has the string 'LOGIN' on it
    case "LOGIN":
      // we get data from the payload object on action
      return { username: action.payload.username, isAuth: true };
    case "SIGNOUT":
      return { username: "", isAuth: false };
    default:
      // if no case matches, return previous state
      return state;
  }
}

React useReducer hook là một cách hữu ích để quản lý state trong React bên cạnh useState, và nó có thể kết hợp với context dùng để quản lý state trong một ứng dụng mà có thể không cần sử dụng đến redux

Ngoài ra, sự kết hợp useReducer và useContext có thể là hệ thống quản lý state cho apps.

const initialState = { username: "", isAuth: false };

function reducer(state, action) {
  switch (action.type) {
    case "LOGIN":
      return { username: action.payload.username, isAuth: true };
    case "SIGNOUT":
      // could also spread in initialState here
      return { username: "", isAuth: false };
    default:
      return state;
  }
}

function App() {
  // useReducer requires a reducer function to use and an initialState
  const [state, dispatch] = useReducer(reducer, initialState);
  // we get the current result of the reducer on 'state'

  // we use dispatch to 'dispatch' actions, to run our reducer
  // with the data it needs (the action object)
  function handleLogin() {
    dispatch({ type: "LOGIN", payload: { username: "Ted" } });
  }

  function handleSignout() {
    dispatch({ type: "SIGNOUT" });
  }

  return (
    <>
      Current user: {state.username}, isAuthenticated: {state.isAuth}
      <button onClick={handleLogin}>Login</button>
      <button onClick={handleSignout}>Signout</button>
    </>
  );
}

Các hooks custom

Các hooks được tạo ra nhằm tái sử dụng behavior dễ dàng giữa các component. Chúng là pattern dễ hiểu hơn các loại khác của class components, như higher-order component hay render props. Chúng ta có thể tự tạo ra hook của riêng mình tùy thuộc vào nhu cầu từng dự án, bên cạnh những gì React đã có sẵn:

// here's a custom hook that is used to fetch data from an API
function useAPI(endpoint) {
  const [value, setValue] = React.useState([]);

  React.useEffect(() => {
    getData();
  }, []);

  async function getData() {
    const response = await fetch(endpoint);
    const data = await response.json();
    setValue(data);
  };

  return value;
};

// this is a working example! try it yourself (i.e. in codesandbox.io)
function App() {
  const todos = useAPI("https://todos-dsequjaojf.now.sh/todos");

  return (
    <ul>
      {todos.map(todo => <li key={todo.id}>{todo.text}</li>}
    </ul>
  );
}

Các quy tắc của hooks:

Khi sử dụng React hooks thì có 2 nguyên tắc không nên vi phạm:

  1. Hooks chỉ có thể đuợc call trên top của components (chúng không thể ở trong conditionals, loops hay nested functions).
  2. Hooks chỉ có thể sử dụng trong function components (chúng không thể dùng trong Javascript function bình thuờng hay class components).
function checkAuth() {
  // Rule 2 Violated! Hooks cannot be used in normal functions, only components
  React.useEffect(() => {
    getUser();
  }, []);
}

function App() {
  // this is the only validly executed hook in this component
  const [user, setUser] = React.useState(null);

  // Rule 1 violated! Hooks cannot be used within conditionals (or loops)
  if (!user) {
    React.useEffect(() => {
      setUser({ isAuth: false });
      // if you want to conditionally execute an effect, use the
      // dependencies array for useEffect
    }, []);
  }

  checkAuth();

  // Rule 1 violated! Hooks cannot be used in nested functions
  return <div onClick={() => React.useMemo(() => doStuff(), [])}>Our app</div>;
}

Có rất nhiều kiến thức React để học hỏi nhưng trên đây là những gì được trình bày tại bài viết này là những lớp gạch cơ bản nhất trên con đường React master trong năm 2023. Keep coding và chờ những bài viết tiếp theo nhé.

Có thể bạn quan tâm:

Xem thêm việc làm về React trên TopDev

TopDev via Freecodecamp

 

71 trích đoạn code Python cho các vấn đề hàng ngày của bạn

71-trich-doan-code-python-cho-cac-van-de-hang-ngay-cua-ban

Để bắt đầu, mình sẽ khởi động bằng cách chuyển tất cả trích đoạn code trong bài viết này tới Jupyter Notebooks (1 nguồn mở cho phép bạn tạo và chia sẻ các live code, phương trình, hiệu ứng hình ảnh cũng như các văn bản, v..v..). Nếu bạn có hứng thú với các loại project như vầy, thì hãy tiếp tục tới the GitHub repo nhé. Mình sẽ rất trân trọng sự giúp đỡ này!

Các Vấn đề thường gặp 

Trong phần này, chúng ta sẽ xem thử các kịch bản phổ biến khác nhau thường phát sinh và làm thế nào để giải quyết chúng với code Python. Đặc biệt, mình sẽ chia sẻ qua lời giải thích ngắn gọn về vấn đề với 1 danh sách các giải pháp với code Python. Sau đó, mình sẽ link tất cả các nguồn tài nguyên mà mình có. 

Tìm việc làm python lương cao

Đảo ngược Dictionary

Đôi khi chúng ta có 1 dictionary trong tay, và muốn có thể nhảy tới đúng key và value của nó. Dĩ nhiên, có nhiều mối lo lắng như “làm cách nào để chúng ta có thể đối phó với các ‘clone value’?” và “sẽ ra sao nếu các value đang không ‘hashable’?”. Điều đó nói rằng, trong các trường hợp đơn giản, sẽ có vài cách hóa giải:

# Use to invert dictionaries that have unique values
my_inverted_dict = dict(map(reversed, my_dict.items()))

# Use to invert dictionaries that have unique values
my_inverted_dict = {value: key for key, value in my_dict.items()}

# Use to invert dictionaries that have non-unique values
from collections import defaultdict
my_inverted_dict = defaultdict(list)
{my_inverted_dict[v].append(k) for k, v in my_dict.items()}

# Use to invert dictionaries that have non-unique values
my_inverted_dict = dict()
for key, value in my_dict.items(): 
  my_inverted_dict.setdefault(value, list()).append(key)

# Use to invert dictionaries that have lists of values
my_dict = {value: key for key in my_inverted_dict for value in my_map[key]}

Để hiểu rõ hơn, hãy xem thử bài viết của mình có tựa đề “How to Invert a Dictionary in Python”. Nó bao gồm cách giải cho mỗi giải pháp, các metric hiệu suất và khi chúng có thể áp dụng được. Ngoài ra, mình còn có 1 video Youtube, cũng cover cùng đề tài này luôn.

Cộng các Element của 2 List

Hãy nói rằng bạn có 2 list, và bạn muốn merge chúng lại với nhau thành 1 list cùng element. Nói cách khác, bạn muốn thêm element đầu tiên của list thứ 1 tới element đầu của list thứ 2 và lưu trữ kết quả trong 1 list mới. Vâng, có vài cách để thực hiện điều này:

ethernet_devices = [1, [7], [2], [8374163], [84302738]]
usb_devices = [1, [7], [1], [2314567], [0]]

# The long way
all_devices = [
  ethernet_devices[0] + usb_devices[0], 
  ethernet_devices[1] + usb_devices[1], 
  ethernet_devices[2] + usb_devices[2], 
  ethernet_devices[3] + usb_devices[3], 
  ethernet_devices[4] + usb_devices[4]
]

# Some comprehension magic
all_devices = [x + y for x, y in zip(ethernet_devices, usb_devices)]

# Let's use maps
import operator 
all_devices = list(map(operator.add, ethernet_devices, usb_devices))

# We can't forget our favorite computation library
import numpy as np 
all_devices = np.add(ethernet_devices, usb_devices)

Nếu bạn muốn hiểu sâu hơn, hãy xem thử bài viết mang tựa đề “Cách tính tổng các phần tử của hai danh sách trong Python” của mình. Vốn đang mang 1 thử thách thú vị. Ngoài ra, bạn cũng có thể nhận vài thứ bổ ích từ video Youtube với cùng tựa đề của mình.

Kiểm tra 1 File có đang Tồn tại

Một trong những đặc quyền tuyệt vời của Python là nhờ sự quản lý các file 1 cách dễ dàng của nó. Không giống như Java, Python có 1 syntax built-in cho việc đọc và viết 1 file.
Kết quả là, kiểm tra 1 file nếu nó đang tồn tại là 1 nhiệm vụ khá đơn giản:

# Brute force with a try-except block (Python 3+)
try: 
  with open('/path/to/file', 'r') as fh: 
    pass
except FileNotFoundError: 
  pass

# Leverage the OS package (possible race condition)
import os 
exists = os.path.isfile('/path/to/file')

# Wrap the path in an object for enhanced functionality
from pathlib import Path
config = Path('/path/to/file') 
if config.is_file(): 
  pass

Chuyển đổi 2 List vào 1 Dictionary

Trước đó, chúng ta đã nói về Gộp 2 List trong Python. Cũng như hóa ra là, có rất nhiều thứ chúng ta có thể làm với 2 list. Ví dụ: chúng ta có thể thử map 1 cái trên 1 cái khác để tạo dictionary.

Cũng nhiều như các vấn đề này, có 1 vài sự lo ngại ở đây. Ví dụ: Sẽ ra sao nếu 2 list không cùng 1 size? Ngoài ra, sẽ ra sao nếu các key không ‘unique’ hay ‘hashable’? Điều đó có nghĩa là, trong vài trường hợp cơ bản, có 1 vài giải pháp đơn giản như sau:

column_names = ['id', 'color', 'style']
column_values = [1, 'red', 'bold']

# Convert two lists into a dictionary with zip and the dict constructor
name_to_value_dict = dict(zip(column_names, column_values))

# Convert two lists into a dictionary with a dictionary comprehension
name_to_value_dict = {key:value for key, value in zip(column_names, column_values)}

# Convert two lists into a dictionary with a loop
name_value_tuples = zip(column_names, column_values) 
name_to_value_dict = {} 
for key, value in name_value_tuples: 
  if key in name_to_value_dict: 
    pass # Insert logic for handling duplicate keys 
  else: 
    name_to_value_dict[key] = value

1 lần nữa, bạn có thể tìm cách giải thích cho mỗi giải pháp trong bài “Cách chuyển đổi 2 danh sách thành từ điển trong Python” của mình. Nếu bạn là 1 người trực quan, bạn có thể sẽ kết video Youtube của mình, với nội dung cover các mapping list tới các dictionaries.

  10 Lý do nên học Python trong năm 2024

Kiểm tra Nếu 1 List đang Trống (Empty)

Nếu bạn đang xài 1 ngôn ngữ thuộc loại  “statical” như Java hay C, bạn có thể lo ngại bởi sự thiếu hụt các loại static trong Python. Mình cam đoan rằng, việc không rành về các lọai của variable có thể gây khó chịu 1 vài lúc, nhưng không sao vì đã có vài “perk” – đặc quyền bên cạnh. Ngay lập tức, chúng ta có thể kiểm tra nếu 1 List đang trống bởi loại cơ động (flexibility) của riêng nó – bên cạnh cách phương pháp khác:

my_list = list()

# Check if a list is empty by its length
if len(my_list) == 0: 
  pass # the list is empty

# Check if a list is empty by direct comparison (only works for lists)
if my_list == []: 
  pass # the list is empty

# Check if a list is empty by its type flexibility **preferred method**
if not my_list: 
  pass # the list is empty

Tạo Bản sao (cloning) cho 1 List

1 trong những đối tượng (subject) yêu thích của mình trong lập trình chính là ‘copy các loại data – copying data types’. Sau tất cả, không có gì là dễ dàng trong thế giới dựa trên sự tương quan – reference-based mà ta đang sống, và điều này cũng tương tự trong Python. May mắn thay, nếu ta muốn copy 1 list, có vài cách để thực hiện điều này:

my_list = [27, 13, -11, 60, 39, 15]

# Clone a list by brute force
my_duplicate_list = [item for item in my_list]

# Clone a list with a slice
my_duplicate_list = my_list[:]

# Clone a list with the list constructor
my_duplicate_list = list(my_list) 

# Clone a list with the copy function (Python 3.3+)
my_duplicate_list = my_list.copy() # preferred method

# Clone a list with the copy package
import copy
my_duplicate_list = copy.copy(my_list)
my_deep_duplicate_list = copy.deepcopy(my_list)

# Clone a list with multiplication?
my_duplicate_list = my_list * 1 # do not do this

Khi nói đến ‘cloning’, việc nhận thức về sự khác biệt giữa các bản sao nông và sâu (shallow & deep copies) khá là quan trọng. May mắn thay (1 lần nữa), mình đã có 1 bài viết cover về chủ đề này.

Khôi phục Mục sau cùng của 1 List

(Retrieving the Last Item of a List)

Vì chúng có chung đề tài của các list, hãy nói về việc lấy mục sau cùng của 1 list. Trong hầu hết các ngôn ngữ, điều này khá liên quan tới một số biểu thức toán học phức tạp liên quan đến độ dài của danh sách. Và nếu mình nói với bạn rằng có vài giải pháp thú vị trong Python thì bạn cảm thấy thế nào?

my_list = ['red', 'blue', 'green']

# Get the last item with brute force using len
last_item = my_list[len(my_list) - 1]

# Remove the last item from the list using pop
last_item = my_list.pop() 

# Get the last item using negative indices *preferred & quickest method*
last_item = my_list[-1]

# Get the last item using iterable unpacking
*_, last_item = my_list

Như trên, đọc thêm bài viết “Cách lấy mục cuối cùng của danh sách trong Python”, kèm theo thử thách, metric hiệu năng và, dĩ nhiên rồi, 1 video Youtube về nó.

  Tại sao bạn nên sử dụng Python Generator

Tạo 1 Lối tắt cho Script Python

Đôi lúc khi bạn tạo 1 script, bạn sẽ muốn để chạy nó 1 cách tiện lợi bởi cú nhấp vào 1 nút. Dĩ nhiên rằng, có vài cách để thực hiện nó.

Đầu tiên, chúng ta có thể tạo 1 lối tắt Windows với thiết lập như sau:

\path\to\trc-image-titler.py -o \path\to\output

Bên cạnh, chúng ta cũng có thể tạo 1 file batch với đoạn code sau đây:

@echo off
\path\to\trc-image-titler.py -o \path\to\output

Cuối cùng, chúng ta có thể tạo 1 script batch với code sau:

#!/bin/sh
python /path/to/trc-image-titler.py -o /path/to/output

Nếu bạn đang tìm giải thích chi tiết hơn, hãy xem thử bài “Cách tạo lối tắt tập lệnh Python với các đối số”.

Sắp xếp 1 List các String

Sắp xếp là 1 task thông thường mà bạn sẽ được mong đợi để biết cách triển khai thực hiện trong ngành Khoa học Máy tính. Cho dù việc sắp xếp các thuật toán trong hầu hết các chương trình giảng dạy cần sự tập trung cao độ, không ai sẽ nói cho bạn biết về độ phức tạp mà việc sắp xếp mang lại. Ví dụ: sắp xếp các con số khá đơn giản, vậy còn sắp xếp các string sẽ ra sao? Làm cách nào để ta quyết định 1 thứ tự thích hợp? Không sao cả, có khá nhiều sự lựa chọn trong Python:  

my_list = ["leaf", "cherry", "fish"]

# Brute force method using bubble sort
my_list = ["leaf", "cherry", "fish"]
size = len(my_list)
for i in range(size): 
  for j in range(size): 
    if my_list[i] < my_list[j]: 
       temp = my_list[i] 
       my_list[i] = my_list[j] 
       my_list[j] = temp

# Generic list sort *fastest*
my_list.sort()

# Casefold list sort
my_list.sort(key=str.casefold)

# Generic list sorted
my_list = sorted(my_list) 

# Custom list sort using casefold (>= Python 3.3)
my_list = sorted(my_list, key=str.casefold) 

# Custom list sort using current locale 
import locale
from functools import cmp_to_key
my_list = sorted(my_list, key=cmp_to_key(locale.strcoll)) 

# Custom reverse list sort using casefold (>= Python 3.3)
my_list = sorted(my_list, key=str.casefold, reverse=True)

Phân tích 1 Bảng tính

(Parsing a Spreadsheet)
1 trong những trường hợp sử dụng thú vị cho Python là vì ‘data science’. Nhưng không may, tuy nhiên, điều đó có nghĩa là bạn phải xử lý rất nhiều ‘raw data’ trong các định dạng khác nhau như file văn bản và bảng tính. May mắn rằng, Python có nhiều tiện ích được built-in cho việc đọc các định dạng file khác nhau. Ví dụ, chúng ta có thể phân tích 1 bảng tính 1 cách dễ dàng bằng cách:  

# Brute force solution
csv_mapping_list = []
with open("/path/to/data.csv") as my_data: 
  line_count = 0 
  for line in my_data: 
    row_list = [val.strip() for val in line.split(",")] 
    if line_count == 0: 
      header = row_list 
    else: 
      row_dict = {key: value for key, value in zip(header, row_list)}
      csv_mapping_list.append(row_dict) 
    line_count += 1

# CSV reader solution
import csv
csv_mapping_list = []
with open("/path/to/data.csv") as my_data: 
  csv_reader = csv.reader(my_data, delimiter=",") 
  line_count = 0 
  for line in csv_reader: 
    if line_count == 0: 
      header = line 
    else: 
      row_dict = {key: value for key, value in zip(header, line)} 
      csv_mapping_list.append(row_dict) 
    line_count += 1

# CSV DictReader solution
import csv
with open("/path/to/dict.csv") as my_data: 
  csv_mapping_list = list(csv.DictReader(my_data))

Trong trường hợp này, chúng ta có thể lấy output của mình trong list các dictionary. Nếu bạn muốn biết thêm về cách hoạt động của việc này, hãy xem bài viết đầy đủ “Cách phân tích bảng tính bằng Python” của mình.

Sắp xếp 1 List các Dictionary

1 khi bạn đã có 1 list các dictionary, bạn có thể sẽ muốn tổ chức chúng trong vài trật tự cụ thể. Ví dụ: nếu các dictionary có 1 key cho date, ta có thể thử sắp xếp chúng theo thứ tự theo niên đại. May thay, sắp xếp là một nhiệm vụ tương đối nhẹ nhàng: 

csv_mapping_list = [
  { "Name": "Jeremy", "Age": 25, "Favorite Color": "Blue" }, 
  { "Name": "Ally", "Age": 41, "Favorite Color": "Magenta" }, 
  { "Name": "Jasmine", "Age": 29, "Favorite Color": "Aqua" }
]

# Custom sorting
size = len(csv_mapping_list)
for i in range(size): 
  min_index = i 
  for j in range(i + 1, size): 
    if csv_mapping_list[min_index]["Age"] > csv_mapping_list[j]["Age"]: 
      min_index = j 
      csv_mapping_list[i], csv_mapping_list[min_index] = csv_mapping_list[min_index], csv_mapping_list[i]

# List sorting function
csv_mapping_list.sort(key=lambda item: item.get("Age"))

# List sorting using itemgetter
from operator import itemgetter
f = itemgetter('Name')
csv_mapping_list.sort(key=f)

# Iterable sorted function
csv_mapping_list = sorted(csv_mapping_list, key=lambda item: item.get("Age"))

Các cách giải và nhiều chú thích hơn trong bài “Cách sắp xếp danh sách từ điển trong Python“.

Viết 1 List Comprehension

1 trong những đề tài Python yêu thích của mình là nói về các list comprehension. Như những ai đã có thời gian dài sử dụng các ngôn ngữ nhu Java, C/C++ và C#, mình chưa từng thấy thứ gì giống như là 1 list comprehension cho tới khi mình tập tành với Python. Giờ đây, mình khá là bị cuốn hút với chúng. Kết quả là, mình đã đặt chúng cùng nhau trong 1 list toàn bộ:

# Define a generic 1D list of constants
my_list = [2, 5, -4, 6]

# Duplicate a 1D list of constants
[item for item in my_list]

# Duplicate and scale a 1D list of constants
[2 * item for item in my_list]

# Duplicate and filter out non-negatives from 1D list of constants
[item for item in my_list if item < 0]

# Duplicate, filter, and scale a 1D list of constants
[2 * item for item in my_list if item < 0]

# Generate all possible pairs from two lists
[(a, b) for a in (1, 3, 5) for b in (2, 4, 6)]

# Redefine list of contents to be 2D
my_list = [[1, 2], [3, 4]]

# Duplicate a 2D list
[[item for item in sub_list] for sub_list in my_list]

# Duplicate an n-dimensional list
def deep_copy(to_copy): 
  if type(to_copy) is list: 
    return [deep_copy(item) for item in to_copy] 
  else: 
    return to_copy

Xem thêm về cách giải thích chính thức về tất cả các code này trong bài “Cách viết 1 danh sách bằng Python”. Như là quà khuyến mãi, mình có 1 video Youtube, vốn chia sẻ vài ví dụ về các list comprehension.

Gộp 2 Dictionary

Trong bộ sưu tập này, ta đã nói rất nhiều về việc xử lý các cấu trúc data như list và dictionary. Vâng, cái này cũng sẽ tương tự. Cụ thể hơn là, chúng ta đang xem về việc gộp 2 dictionary lại với nhau. Dĩ nhiên, việc hợp nhất 2 dictionary sẽ mang vài rủi ro. Ví dụ: Nếu có các key bị lặp (duplicate key) thì sẽ ra sao? Thật may vì ta sẽ có các giải pháp cho nó:

yusuke_power = {"Yusuke Urameshi": "Spirit Gun"}
hiei_power = {"Hiei": "Jagan Eye"}
powers = dict()

# Brute force
for dictionary in (yusuke_power, hiei_power): 
  for key, value in dictionary.items(): 
    powers[key] = value

# Dictionary Comprehension
powers = {key: value for d in (yusuke_power, hiei_power) for key, value in d.items()}

# Copy and update
powers = yusuke_power.copy()
powers.update(hiei_power)

# Dictionary unpacking (Python 3.5+)
powers = {**yusuke_power, **hiei_power}

# Backwards compatible function for any number of dicts
def merge_dicts(*dicts: dict): 
  merged_dict = dict() 
  for dictionary in dicts: 
    merge_dict.update(dictionary) 
  return merged_dict

Nếu bạn cũng thích, mình có 1 bài vốn cover ngay đúng đề tài này với tên “Cách hợp nhất 2 từ trong Python”, kèm theo 4 giải pháp cũng như metric hiệu năng.

Định dạng 1 String

Dù thích hay không, chúng ta cũng sẽ tự thấy rằng mình mình hay vùi dập các lệnh print xuyên suốt dòng code cho mục đích debug nhanh hơn. Sau tất cả, 1 lệnh print được đặt đúng chỗ có thể giúp bạn tiết kiệm được khá nhiều thời gian. Tuy nhiên, không phải lúc này cũng dễ dàng và tiện lợi để hiển thị đúng thứ ta muốn. Nhưng không sao cả, Python có khá nhiều lựa chọn cho ‘format’: 

name = "Jeremy"
age = 25

# String formatting using concatenation
print("My name is " + name + ", and I am " + str(age) + " years old.")

# String formatting using multiple prints
print("My name is ", end="")
print(name, end="")
print(", and I am ", end="")
print(age, end="")
print(" years old.")

# String formatting using join
print(''.join(["My name is ", name, ", and I am ", str(age), " years old"]))

# String formatting using modulus operator
print("My name is %s, and I am %d years old." % (name, age))

# String formatting using format function with ordered parameters
print("My name is {}, and I am {} years old".format(name, age))

# String formatting using format function with named parameters
print("My name is {n}, and I am {a} years old".format(a=age, n=name))

# String formatting using f-Strings (Python 3.6+)
print(f"My name is {name}, and I am {age} years old")

Hãy nghĩ rằng các giải pháp này không cần phải được dùng với lệnh print. Nói cách khác, bạn cứ thoải mái sử dụng các cách giải này như f-string bất kỳ đâu mà bạn cần.

Bài viết “Cách định dạng chuỗi trong Python” sẽ giúp bạn hiểu rõ thêm. Nếu bạn thích dùng các ‘snippet’ này, đừng quên check video Youtube của mình, có tựa là “6 Ways to Format a String in Python Featuring My Cat”.

Print trên cùng 1 Dòng

Đi cùng với dòng tương tự như việc định dạng các string, đôi lúc bạn chỉ cần để print trên cùng 1 dòng trong Python. Cũng như lệnh “ print “ hiện tại được thiết kế, nó tự động áp dụng 1 dòng mới tới cuối dòng string của bạn. May thay, có 1 vài cách bên cạnh đó:

# Python 2 only
print "Live PD",

# Backwards compatible (also fastest)
import sys
sys.stdout.write("Breaking Bad")

# Python 3 only
print("Mob Psycho 100", end="")

Xem thêm về các giải pháp này tại bài viết “Cách In trên cùng 1 dòng trong Python” cho các cách sử dụng khác và caveat của nó. 

Kiểm tra Hiệu năng

Cuối cùng, bạn đôi lúc chỉ muốn so sánh 1 vài đoạn code. Và Python có vài sự lựa chọn đơn giản dành cho bạn: 

# Brute force solution
import datetime
start_time = datetime.datetime.now()
[(a, b) for a in (1, 3, 5) for b in (2, 4, 6)] 
# example snippet
end_time = datetime.datetime.now()
print end_time - start_time

# timeit solution
import timeit
min(timeit.repeat("[(a, b) for a in (1, 3, 5) for b in (2, 4, 6)]"))

# cProfile solutionimport cProfile
cProfile.run("[(a, b) for a in (1, 3, 5) for b in (2, 4, 6)]")

Đừng quên xem thêm bài viết đầy đủ “Cách kiểm tra hiệu suất mã python” này nhé.

Dịch từ therenegadecoder.com

Xem thêm các bài viết hay về Python tại đây:

Cơ hội việc làm Software hấp dẫn tại TopDev đang chờ bạn!

FE CREDIT “PHÁ LỆ” TUYỂN DỤNG MẠNH – HOÀN THÀNH VÒNG 2 CỦA CHƯƠNG TRÌNH TECHSPEC LỚN NHẤT NĂM 2020

Sáng ngày 8 tháng 2, các ứng viên đã có mặt để tham gia vòng Test của chương trình TECHSPEC 2020. Chương trình được tổ chức bởi FE CREDIT, dành cho các bạn trẻ trong lĩnh vực CNTT muốn thử thách bản thân để bứt phá trong sự nghiệp, nắm bắt mọi cơ hội, học hỏi, cập nhật những công nghệ tiên tiến nhất, và đạt được kỹ năng chuyên môn thông qua những dự án cùng đội ngũ chuyên nghiệp tại FE CREDIT.

Được giới chuyên môn đánh giá cao, TECHSPEC 2020 là một trong những chương trình tuyển dụng được đầu tư nhiều nhất trong năm 2020, với lộ trình đào tạo chuyên sâu và mở ra cơ hội phát triển sự nghiệp bền vững tại , hứa hẹn chính là sự lựa chọn tốt nhất cho các bạn lập trình viên mong muốn phát triển kỹ năng một cách toàn diện. Tại buổi tuyển dụng, Anh Hà Nguyễn Khánh Luân – Leader của IT BA tại FE CREDIT chia sẻ:

“FE CREDIT cần xây dựng những sản phẩm có thể cạnh tranh với các công ty đối thủ, những quy trình mà có thể giảm thiểu được độ “cồng kềnh” và phức tạp của một công ty FinTech. Đây là môi trường tốt nhất để đào tạo các bạn chưa có kinh nghiệm hay fresher. Khái niệm bên mình khá rõ ràng, khả năng bạn đến đâu và có thể improve những gì? Bên mình vẫn chào đón những bạn developer chuyển ngành sang BA. Khi bạn có mindset, có kiến thức và logic về lập trình và hệ thống, mà chỉ hơi yếu về quy trình, hay giải pháp, kiến trúc về hệ thống thì FE CREDIT có thể đào tạo những điều này. Điều quan trọng nhất chính là nằm ở mindset. Với một mindset tốt các bạn có thể sẽ nhanh chóng đạt được sự trưởng thành ở các mặt, trưởng thành hơn trong việc ra quyết định, giải pháp, cũng như trưởng thành trong các kỹ năng giao tiếp quan trọng.”

Với nhu cầu và sự thay đổi chóng mặt của thị trường fintech ngày càng cạnh tranh một cách khốc liệt, đòi hỏi các công ty càng phải nâng cấp công nghệ để phù hợp với xu hướng chuyển đối số, FE CREDIT cũng không nằm ngoài cuộc chơi đó. Trong những năm vừa qua, FE CREDIT đã có những bước tăng trưởng vượt bậc bằng những sản phẩm công nghệ đột phá của mình. Vì vậy nhu cầu tuyển dụng nhân sự trong lĩnh vực này càng gia tăng mạnh mẽ với 4 nhóm nghề chính bao gồm Software Development, Business Analysis, Project Management, và Quality Assurance. Đại diện bộ phận tuyển dụng phía FE CREDIT cho biết:

“Đây là lần đầu tiên FE CREDIT giới thiệu chương trình đến TECHSPEC đến với các bạn ứng viên trong cộng đồng công nghệ thông tin. Vì vậy, chương trình vẫn còn khá mới mẻ đối với các bạn. Đa phần các bạn ứng viên trong đợt tuyển dụng này đều rất tiềm năng. FE CREDIT là một trong những tổ chức tiên phong trong việc ứng dụng các công nghệ mới với nhiều dự án liên phòng ban. Vì vậy, chúng tôi mong muốn tìm kiếm các ứng viên có tinh thần ham học hỏi, sẵn sàng đón nhận các thử thách mới, có khả năng làm việc nhóm và thích ứng nhanh.”

Đây cũng được xem là một trong những chương trình hiếm hoi về Tech tại Việt Nam, tuyển dụng lượng nhân sự lớn dưới 3 năm kinh nghiệm với nhiều vòng thi tuyển lựa chọn kỹ lưỡng. Vượt qua hàng trăm hồ sơ, đã có rất nhiều ứng viên ứng tuyển thành công, họ là những người từng làm việc tại nhiều công ty và các dự án lớn. Bạn Bùi Thiên Hương – ứng viên vị trí Project Management cho biết:

“Mình cảm thấy quy trình tuyển dụng của 2 vòng vừa qua rất bài bản và chuyên nghiệp, ấn tượng với những phúc lợi cũng như những cơ hội nghề nghiệp mà các anh chị nhân sự chia sẻ. Mình rất muốn được bắt tay vào công việc tại đây để được trải nghiệm những dự án . Sau này nếu được mình cũng muốn học được đủ kiến thức để có thể tự lead một dự án của riêng mình tại FE CREDIT như các anh chị Senior khác tại đây.”

Trong thời điểm hiện tại, ngành IT nói chung và ngành FinTech nói riêng đang được đánh giá là một trong những ngành mũi nhọn cho Việt Nam. Hơn 153 cơ sở tổ chức đào tạo được khoảng 50,000 nhân sự cho ngành IT. Tuy nhiên vẫn có sự thiếu hụt mạnh do việc đào tạo chưa thực sự đáp ứng được toàn bộ nhu cầu của doanh nghiệp, vì vậy các doanh nghiệp lớn như FE CREDIT đã chủ trương tuyển dụng nhiều Junior hơn nhằm đào tạo vào xây dựng một đội ngũ trong tương lai. Bạn Hồ Đông Triều – ứng viên Software Development chia sẻ tại buổi thi tuyển:

“Hiện tại mình đang muốn thử sức với lĩnh vực tài chính cụ thể là ứng dụng công nghệ mobile vào FinTech. Mình tin rằng FE Credit sẽ là một một trường tốt giúp mình có thể có được nhiều kiến thức hơn về việc kết hợp giữa tài chính và công nghệ. Trong tương lai mình hy vọng sẽ phấn đấu để có thể trở thành leader của một team software tại FE CREDIT.”

Sau hơn 2 tháng tuyển dụng, vòng 2 của chương trình TECHSPEC 2020 cũng đã kết thúc tốt đẹp. Vòng phỏng vấn sẽ diễn ra vào cuối tháng 2, các ứng viên sẽ sớm có tin vui trong kỳ tuyển dụng năm nay. 

Timeline các vòng tuyển dụng của TECHSPEC 2020

  • Nộp hồ sơ: 2/12/2019 – 17/01/2020
  • Kiểm tra năng lực: 10/02/2020 – 14/02/2020
  • Phỏng vấn: 17/02/2020 – 29/02/2020
  • Thông báo kết quả: 3/2020
  • Bắt đầu làm việc: 4/2020

Contact:

Mobile: (028) 3911 5212 – Ext: 10594

Facebook: FE CREDIT CAREER HUB

Tạo hiệu ứng trong react với React Spring

Trong bài viết này chúng ta sẽ tìm hiểu về cách tạo animation với thư viện React Spring cho các element trong React

Cài đặt

Để bắt đầu chúng ta khởi tạo project với create-react-app trong command line tool.

create-react-app react-animations

Sau đó chúng ta di chuyển đến thư mục react-animations và cài đặt react spring library.

cd react-animations
npm i react-spring
npm start

Tạo hiệu ứng đầu tiên với Spring component

Trong App.js ta xoá đi các dòng code mặc định ban đầu và thêm vào đoạn code bên dưới:

import React from "react";
import { Spring } from "react-spring";
import "./styles.css";

function App() {
  return (

   <Spring
     from={{ opacity: 0.6, marginTop: -50 }}
     to={{ opacity: 1, marginTop: 50 }}
   >
     {props => (
        <div style={props} className="App">
          <article className="post">
            <h1>My first posts</h1>
            <p>
              Lorem ipsum dolor sit amet consectetur adipisicing elit.
              Cupiditate rerum reprehenderit consectetur porro similique
              reiciendis ex consequuntur tempore! Similique, pariatur
              harum.Facilis, accusantium quam labore incidunt soluta
              suscipit ipsa omnis.
            </p>
          </article>
        </div>
      )}
    </Spring>
  );
}

export default App;

Trong đoạn code trên chúng ta import component Spring từ react-spring. Trong component Spring ta sử dụng 1 props là function trả về React Element, và component Spring sẽ render giá trị trả về của hàm đó thay vì trực tiếp render React Element như thông thường.

Sau đó truyền props parameter vào style attribute của thẻ div.

Với Spring component chúng ta sẽ tạo hiệu ứng di chuyển các elements và nó nhận vào 2 props khác là from (initial value) và to (final value):

From: vị trí ban đầu.

To: vị trí khi kết thúc hiệu ứng

Tạo hiệu ứng cho nhiều elements

Để có thể tạo hiệu ứng cho nhiều component spring cung cấp một component là Trail.

Tạo component AllPosts và import Trail từ react-spring theo ví dụ sau đây:

import React from "react";
import { Trail } from "react-spring";
import "./styles.css";
 
const posts = [
  { title: "My first post", id: 1 },
  { title: "My second post", id: 2 },
  { title: "My Third post", id: 3 },
  { title: "My Fourth post", id: 4 }
];
 
function AllPosts() {
  return (
 
   <Trail
     items={posts}
     keys={post => post.id}
     from={{ marginLeft: -20, opacity: 0 }}
     to={{ marginLeft: 20, opacity: 1 }}
   >
     {post => props => (
        <div style={props} className="post">
             {post.title}
        </div>
      )}
    </Trail>
  );
}
 
export default AllPosts

Trail nhận vào 4  props là items, keys, from và to.

Items: là array các element chúng ta cần tạo hiệu ứng

keys: là unique key prop cho mỗi item trong array.

Spring config

Spring cho phép chúng ta  điều chỉnh các thông số delays, tension, friction, resets props là config

Ngoài ra bạn có thể sử dụng các preset có sẵn của spring thay vì chỉnh tay từng thông số

Property Value
config.default { tension: 170, friction: 26 }
config.gentle { tension: 120, friction: 14 }
config.wobbly { tension: 180, friction: 12 }
config.stiff { tension: 210, friction: 20 }
config.slow { tension: 280, friction: 60 }
config.molasses { tension: 280, friction: 120 }

Ví dụ: chúng ta tạo component navbar và sử dụng preset slow của Spring component để tạo hiệu ứng cho thanh navbar như sau:

import React from "react";
import ReactDOM from "react-dom";
import {Spring,config} from "react-spring";
import "./styles.css";
 
function NavBar() {
  return (
 
   <Spring from={{ number: 0 }} to={{ number: 100 }} config={config.slow}>
     {props => (

       <div style={{ width: props.number + "%" }}>
         <nav className="nav-bar">
            <a href="#">Home</a>
            <a href="#"> Posts</a>
            <a href="#">Contact</a>
          </nav>
        </div>
      )}
    </Spring>
  );
}
 
export default NavBar;

Kết luận:

Trên đây là những ví dụ cơ bản về cách sử dụng thư viện React Spring thông qua render props api. Thư viện React spring có thể đáp ứng hầu hết các hiệu ứng liên quan đến UI của bạn. Nó cung cấp cho bạn các công cụ đủ linh hoạt để tự tin đưa ý tưởng của bạn vào các chuyển động trên giao diện.

Tuyển dụng React lương cao mới nhất trong tháng

TopDev via Devnow

Cùng một công việc, Java đòi hỏi nhiều dòng code hơn Python?

Vì sao những ngôn ngữ như Java đòi hỏi phải code nhiều hơn Python khi thực hiện cùng một công việc?

Có rất nhiều lí do, nhưng đầu tiên thì tôi muốn nói rõ là tôi đã sử dụng Python một cách thành thạo được 10 năm rồi. Tôi không có vấn đề gì với Python cả, thậm chí tôi còn thấy nó rất tốt cho việc học.

“Code dài hơn hơn khi thực hiện cùng một công việc”

Đúng vậy. Nói chung là lập trình Java sẽ cần phải gõ nhiều hơn Python đấy. Nhưng nếu đó là vấn đề lớn với bạn, hãy kiếm IDE khác tốt hơn hoặc học cách đánh máy nhanh hơn.
Code của Python ngắn gọn hơn, nhưng cái giá phải trả là gì?
Cùng xem nhé…
def getCustomer(id):
Hàm này sẽ trả về gì nhỉ? Hay không trả về bất cứ gì cả? Biến “id” là kiểu gì? Số? Chuỗi? “CustomerID” object?
Cùng xem Java nào…
public CustomerRecord getCustomer(CustomerID id){
Số lượng code dài gấp đôi. Nhưng chúng ta có thể thấy rõ là hàm sẽ trả về kiểu nào, code làm gì trong đó. Những đoạn code của Java rõ ràng như những điều khoản được viết ra trong hợp đồng vậy. Đây điều mà Python không có.
Tôi nghĩ là Python phù hợp cho việc học và có thể làm nhiều thứ cao cấp hơn nữa. Nhưng công việc gần nhất của tôi về Python đòi hỏi viết hơn 100,000 dòng code.
Ứng tuyển các vị trí việc làm Java lương cao trên TopDev
Trong 100,000 dòng code đó, ví dụ mà tôi đưa ra ở trên sẽ xảy ra hơn 1000 lần, với hơn 1000 biến không xác định và hơn 1000 kết quả trả về cũng không xác định luôn. Chúng sẽ không được kiểm tra tại thời gian biên dịch mà là vào lúc chạy chương trình, đây là sự khác biệt quan trọng. Bug trong Python sẽ không được tìm thấy lúc biên dịch. Đọc lại lần nữa đi. Đây. thực. sự. là. một. vấn. đề. lớn.
Bên cạnh việc kiểm tra kiểu dữ liệu thì Threading là 1 trong những nguyên nhân chính vì sao người ta lại sử dụng Java.
  Hiểu hơn về cách hoạt động của JavaScript Engine

Python không hỗ trợ Threading một cách hiệu quả.

Lần đầu tiên đọc về nó, tôi cho rằng mình đã nhẫm lẫn chỗ nào rồi. Nhưng tiếc là tôi chả đọc sai gì cả. Tôi sẽ kết thúc câu trả lời này bằng lời khuyên của mình…
Đừng quá tôn thờ bất kỳ ngôn ngữ nào cả, chúng không phải 1 thứ tôn giáo, chỉ là những công cụ mà thôi. Vài ngôn ngữ tốt hơn những ngôn ngữ khác ở mặt này, nhưng lại thua kém ở các mặt khác.
Java không phải ác quỷ. Nó không hề tệ. Đơn giản là nó khác biệt với Python. Có việc thì nó thực hiện tốt hơn, có việc thì nó tệ hơn.
Tôi nghĩ việc có sự gắn bó cảm xúc với ngôn ngữ đầu tiên bạn học là khá phổ biến. Nhưng cái cảm xúc đó sẽ ngăn cản bạn trở thành một lập trình viên chuyên nghiệp.
Có thể bạn quan tâm:
Xem thêm việc làm Python tại TopDev
Tác giả: Moray Taylor
TopDev via Quora