Trong bài viết này, mình sẽ hướng dẫn các bạn cài Flutter SDK lên Linux, cụ thể mình sử dụng distro phổ biến nhất của Linux là Ubuntu để hướng dẫn các bạn. Trước khi bắt đầu, do mình đã có video trên Youtube, và trong bài hướng dẫn cho Windows mình cũng đã hướng dẫn khá chi tiết nên trong bài viết này, những thao tác nào giống mình sẽ nói nhanh qua thôi. Con bây giờ thì bắt đầu thôi!
Đầu tiên bạn truy cập trang web flutter.dev, bấm vào Get Started, tiếp theo bạn chọn Linux. Ở trang này, bạn tìm đến mục “Get the Flutter SDK”, sau đó bấm vào nút download Flutter SDK ngay bên dưới mục 1 của mục “Get the Flutter SDK” để tải Flutter SDK về. Các bạn chờ tải về xong và mở file bằng Archive Manager hoặc chọn Open with Archive Manager cũng được.
Sau khi mở file bằng Archive Manager, các bạn chọn Extract.
Tiếp tục, các bạn chọn đường dẫn để giải nén folder “flutter” ra. Mình sẽ chọn giải nén ra thư mục “/home/khiemle/Development”. Sau khi giải nén xong, các bạn mở Terminal (ctrl + alt + T), nhập lệnh “sudo gedit ~/.bashrc” và enter, sau đó nhập mật khẩu. Một cửa sổ editor sẽ hiện lên để bạn chỉnh sửa file .bashrc.
Các bạn thêm đoạn sau vào cuối file .bashrc: export PATH=”$PATH:[PATH_TO_FLUTTER_GIT_DIRECTORY]/flutter/bin”. Các bạn sửa [PATH_TO_FLUTTER_GIT_DIRECTORY] thành đường dẫn chứa folder flutter của bạn. Ví dụ trong trường hợp của mình là: export PATH=”$PATH:/home/khiemle/Development/flutter/bin”.
Mục đích của việc trên là để thêm flutter vào trong $PATH để có thể sử dụng command flutter trong terminal mà không cần mỗi lần mở terminal mới phải export mất thời gian, và cũng để cho các ứng dụng khác ví dụ như VS Code có thể sử dụng được command flutter. Sau khi đã thêm flutter vào $PATH xong, các bạn nhấn Save để lưu lại và tắt nó đi.
Tiếp theo, các bạn tắt các cửa sổ Terminal hiện tại đi, mở một cửa sổ terminal mới, gõ lệnh sau: flutter –version. Nếu như nó hiện thông tin về Flutter SDK, Dart version… thì bạn đã cài thành công SDK. Nếu như máy bạn chưa cài đặt Git, khi bạn chạy lệnh trên, nó sẽ báo là “Unable to find git in your PATH”, lúc này, bạn chỉ cần chạy câu lệnh “sudo apt-get install git -y” là được. Sau khi cài git, bạn có thể chạy câu lệnh flutter bình thường.
Lời kết
Vậy là qua bài viết này, mình đã hướng dẫn các bạn cài đặt Flutter SDK trên Linux (Ubuntu distro). Nếu các bạn có thắc mắc gì có thể để lại bình luận bên dưới video hoặc bài viết này để mình có thể giúp các bạn nha. Bạn nào chưa biết cách cài trên Windows đừng quên tham khảo bài viết này nha. Đừng quên chia sẻ cho mọi người nếu bạn thấy hay! Cảm ơn các bạn đã theo dõi bài viết!
Nếu các bạn thích đọc hướng dẫn và tài liệu bằng tiếng anh và ví dụ cụ thể thì có thể tham khảo các nguồn sau đây mình cũng học và tham khảo trên các trang này:
Để download gói cài đặt các bạn vào trang này để lấy gói phù hợp với máy các bạn: https://golang.org/dl/
Cài đặt Golang trên Windows.
Cài đặt
Các bạn vào trang download của Golang mình đã để link phía trên và tím đến chỗ dowload gói cài đặt cho Microsoft Windows, tiến hành download về thôi.
Cũng như cài đặt các chương trình khác các bạn nếu không muốn điều chỉnh gì thì cứ nhấn Next, next nhé.
Chấp nhận điều khoản, nhấn Next.
Ở bước này các bạn có thể điều chỉnh lại đường dẫn mà chúng ta muốn cài đặt Golang trên máy, sau đó nhấn Next.
Nhấn Install nào hỏi nhiều quá đấy.
Các bạn chờ trong giây lát nhé, 1 xí nữa thôi là xong rồi.
Ok, nhấn Finish để hoàn thành việc cài đặt.
Như vậy chúng ta đã hoàn tất cả các bước cài đặt, bước tiếp theo là setup biến môi trường GOROOT và GOPATH.
Thiết lập biến môi trường.
Các bạn nhấn nút Windows và gõ tìm đến cài đặt biến môi trường như hình bên dưới.
Chọn vào Environment Variables.
Sau đó chọn New và thêm một biến (variable) tên là GOPATH với đường dẫn đến thư mục chúng ta muốn tạo các project Golang, đây là nơi làm việc với code Go của chúng ta.
Và nhìn xuống biến có tên Path để xem đã dẫn tới thư mục bin của Go mà ban đầu chúng ta đã cài đặt chưa nhé.
Mình thấy nó đã có rồi nên là ok, nếu chưa có thì mình sẽ thêm vào.
Sau đó restart lại máy và kiểm tra Go đã được setup đúng chưa.
Kiểm tra version của Go bằng command go version
Kiểm tra cấu hình cài đặt biến môi trường lưu ý 2 biến là GOROOT và GOPATH.
Cài đặt Golang trên Ubuntu.
Truy cập vào máy Ubuntu thực hiện việc update apt.
sudo apt-get update
sudo apt-get -y upgrade
Tải Golang về máy.
Vào thư mục mà chúng ta muốn ở đây mình sẽ bỏ vào thư mục /home/vxphong/go và thư mục này mình đã tạo sẵn trên máy.
Sau khi đã tải được gói cài đặt xuống ta tiến hành giải nén (extract).
Ở đây mình cái nhiều phiên bản nên mình sẽ đổi tên thư mục go vừa extract ra thành số version nha.
mv go 1.12.7
Để gọn trông nhìn sạch hơn mình xóa luôn file tar.gz nha, và đây là thư mục mà mình đã cài đặt Go.
cd 1.12.7
pwd
Chúng ta sẽ nhớ đường dẫn này để tí nữa cài đặt biến môi trường GOROOT nha.
/home/vxphong/go/1.12.7
Tạo ra thư mục làm việc với code Go
mkdir /home/vxphong/gopath
Tiếp theo chúng ta hãy thiết lập GOPATH và GOROOT
nano /home/vxphong/.bashrc
Và export các đường dẫn để thiết lập biến môi trường GOPATH, GOROOT và PATH.
export GOROOT=/home/vxphong/go/1.12.7
export GOPATH=/home/vxphong/gopath
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
Như vậy là đã xong kiểm tra version của Go nào các bạn, chúng ta tắt terminal đang làm việc đi và mở lại.
go version
Kiểm tra các biến môi trường đã thiết lập trước đó.
go env
Hello world trong Golang.
Quay trở lại với máy Windows của mình, các bạn nhớ lúc trước mình đá thiết lập biến môi trường GOPATH không? Trong thư mục GOPATH thì chúng ta tạo thêm thư mục src nữa nha. nơi làm việc để tạo ra các project sẽ nằm ở đó.
mkdir src
cd src
mkdir hello-world
Tạo file main.go trong thư mục hello-world với nội dung sau:
package main
import "fmt"
func main(){
fmt.Println("Hello World")
}
Bây giờ chúng ta tiến hành code và chạy chương trình Hello-World thôi nào
Dùng câu lệnh sau đây để chạy chương trình.
go run main.go
Tóm Lại:
Thư mục làm việc sẽ nằm trong thư mục src nằm ở đường dẫn GOPATH do chúng ta đã cài đặt trước đó. Các bạn có thấy các thư mục như github.com, golang.org không đó là nơi chứa các thư viện mà chúng ta sẽ cài đặt để sử dụng cho project. Có thể những bài tiếp theo mình sẽ hướng dẫn các bạn cái thư viện như thế nào nha, nhưng thật ra nó rất đơn giản thôi à chỉ cần 1 câu lệnh là nó sẽ tự động cài và bỏ vào trong các thư mục tương ứng, chỉ cần import để lấy ra sử dụng thôi.
Bài viết được sự cho phép của tác giả Chung Nguyễn
Hôm nay, nhóm Laravel đã phát hành một phiên bản chính mới của “laravel/installer” bao gồm hỗ trợ khởi động nhanh các dự án Jetstream. Với phiên bản mới này khi bạn chạy laravel new project-name, bạn sẽ nhận được các tùy chọn Jetstream. Ví dụ:
React Props Cheatsheet: 10 Patterns mà bạn nên biết (Phần 2)
Tác giả: Reed Barger
6. Cập nhật giá trị của React prop thông qua thanh trạng thái
Để chuyển một giá trị prop cho một thành phần, trong các thành phần của hàm đó cần cố gắng thay đổi ngay lập tức giá trị của prop.
Giá trị prop phải là giá trị thuần túy. Nói cách khác, chúng không thể bị biến dạng hoặc thay đổi trực tiếp. Trong React nếu chúng ta muốn thay đổi các giá trị theo thời gian, thì phương tiện thích hợp để làm điều đó là với status – trạng thái.
Nếu chúng ta muốn chuyển một giá trị prop vào một thành phần và thay đổi nó sau này, có thể đưa nó vào một hook React trạng thái để lưu trữ giá trị đó dưới dạng một biến. Sau đó, cập nhật bằng cách sử dụng chức năng setter thích hợp. Ví dụ, chúng ta có thể làm như vậy với useStatehook hoặc useReducerhook.
Điều gì sẽ xảy ra nếu chúng ta có một đối tượng gồm các thuộc tính mà chúng ta muốn chuyển xuống dưới dạng các giá trị prop độc lập? Tuy nhiên, đối tượng này có rất nhiều thuộc tính. Chúng ta có cần tạo thủ công các đạo cụ riêng lẻ và đặt giá đỡ thành object.propertyName không?
Thay vì làm điều đó cho mọi thuộc tính, chúng ta có thể rất dễ dàng lấy đối tượng và trải rộng các thuộc tính của nó xuống một thành phần dưới dạng các giá trị chống đỡ riêng lẻ bằng cách sử dụng toán tử mở rộng đối tượng {...myPropObject}.
Bằng cách sử dụng cú pháp này – một tính năng trong ES7, cho phép tự động tạo các đạo cụ riêng lẻ theo tên thuộc tính của đối tượng mà không cần phải viết tất cả các tên thuộc tính đó. Nó rất thuận tiện khi làm việc với các đối tượng lớn với nhiều thuộc tính mà chúng ta muốn chuyển dưới dạng các đạo cụ riêng lẻ cho một thành phần.
8. React props có thể tạo ra default value nếu không có giá trị nào được cung cấp
Điều gì sẽ xảy ra nếu ta đã biết rằng chúng mình đang chuyển một chỗ dựa cho một thể hiện của một thành phần, nhưng chúng ta không chuyển chỗ dựa đó cho một instance khác của nó?
Hoặc có thể biết rằng chỗ dựa có thể không có giá trị. Cần làm gì để cung cấp cho nó một giá trị mặc định thay vì chỉ một giá trị undefined nếu không có giá trị prop nào được chuyển cho nó?
Nếu chúng ta đang sử dụng hàm destructuring để truy cập prop đó trong thành phần hàm của mình, chúng ta có thể sử dụng toán tử bằng để cung cấp cho nó một giá trị mặc định. Vì vậy, nếu không có giá trị prop nào được chuyển cho prop đó, chúng ta có thể sử dụng toán tử bằng bên cạnh nó và đặt nó thành giá trị mặc định tốt hơn.
Đặt một giá trị mặc định là rất quan trọng vì giá trị mặc định bình thường cho một giá trị là undefined. Điều này có thể giúp tránh các lỗi có thể xảy ra do default value không ở đó.
9. React props có thể đặt lại tên để tránh errors
Ngoài ra, nếu có xung đột khi đặt tên với một trong các đạo cụ thì sao? Điều gì sẽ xảy ra nếu chúng ta sử dụng một tên prop trên nhiều thành phần và thấy rằng có một giá trị khác trong thành phần của chúng ta có cùng tên biến?
Thay vì phải đi vòng quanh và đổi tên tất cả các giá trị prop trên tất cả các phiên bản của các thành phần, chúng ta chỉ cần sử dụng dấu hai chấm sau tên prop đó, nếu chúng ta đang destructure nó, để đặt cho nó một tên gọi riêng biệt.
Nói cách khác, bạn có thể đặt cho nó một cái tên khác chỉ trong trường hợp đó. Điều này sẽ tránh được xung đột đặt tên cũng như lỗi.
10. Không nên destructure React props nhiều lần
Nếu chúng ta đang destructure một đối tượng từ các props, hãy lưu ý rằng có thể destructure prop đó sâu hơn nữa, tùy vào các thuộc tính cấu thành của nó. Tuy nhiên, thông thường không nên làm như vậy trừ khi bạn rất tin tưởng rằng đối tượng đó sẽ luôn có những thuộc tính đó.
Nếu một trong những thuộc tính đó bị thiếu và bạn cố gắng destructure nó ở nhiều cấp độ chuyên sâu, nó có thể gây ra cho bạn một lỗi khá khó chịu khi bạn đang cố gắng truy cập vào một thuộc tính không tồn tại.
Lưu ý rằng bạn có thể sử dụng destructure bao nhiêu tùy thích, nhưng nó có thể khó đọc đến một điểm nhất định và nó cũng có thể không đáng tin cậy. Nếu bạn đang cố gắng truy cập một thuộc tính trên một đối tượng, có thể không tồn tại, nó sẽ gây ra lỗi.
Trong các bài viết trước chúng ta đã cùng tìm hiểu về các loại Exchange trong RabbitMQ. Có một câu hỏi đặt ra là có thể thực hiện binding một Exchange đến Exchange khác hay không? Câu trả lời là có và chúng ta sẽ thấy cách thực hiện như thế nào trong phần tiếp theo của bài viết này.
Sơ đồ bên dưới là sự kết hợp của sơ đồ ở bài viết Topic Exchange và Header Exchange. Điểm khác biệt duy nhất là cách mà Header Exchange binding tới Topic Exchange với routing key pattern “#.gpcoder.com“.
Một Producer publish một Message đến source Exchange với một routing key dựa trên loại của Exchange. Trong trường hợp này là GPCoderTopicExchange.
Có 2 Queue: QJava và QAll được binding tới Exchange GPCoderTopicExchange lần lượt với routing key patter:
QJava : Queue này sẽ nhận tất cả message có Key match với routing key “java.*.gpcoder.com“. Nghĩa là chỉ nhận các message cho một topic Java cụ thể từ gpcoder.com, chẳng hạn: java.core.gpcoder.com, java.collection.gpcoder.com.
QAll : Queue này nhận tất cả message có Key match với routing key “#.gpcoder.com“. Nghĩa là nhận tất cả message từ gpcoder.com, chẳng hạn: design-pattern.gpcoder, java.gpcoder.com, creational.design-pattern.gpcoder.com.
Một Exchange khác là GPCoderHeadersExchange binding tới Exchange GPCoderTopicExchange với routing key pattern #.gpcoder.com.
Có 3 Queue: QDeveloper, QManager và QPublished được binding tới Exchange GPCoderHeadersExchange với các header:
QDeveloper : Queue này sẽ nhận tất cả message có header là {“dev”, “Developer Channel”} hoặc {“general”, “General Channel”}.
QManager : Queue này nhận tất cả message có header là {“dev”, “Developer Channel”} hoặc {“general”, “General Channel”} hoặc {“manager”, “Manager Channel”}.
QPublished : Queue này nhận tất cả message có header là {“dev”, “Developer Channel”} và {“access”, “publish”}.
Bây giờ nếu một Message được gởi tới Exchange với routing key là design-pattern.gpcoder.com và header là {“dev”, “Developer Channel”} sẽ được chuyển tới Queue QAll và GPCoderHeadersExchange. Sau đó GPCoderHeadersExchange sẽ chuyển đến Queue QDeveloper và QManager dựa vào header {“dev”, “Developer Channel”}.
Tương tự vậy, chúng ta cũng có thể định nghĩa một hay nhiều Exchange khác, chẳng hạn GPCoderDirectExchange binding tới GPCoderHeadersExchange và GPCoderTopicExchange.
Một Exchange nhận Message từ publisher được gọi là Source Exchange. Trong sơ đồ trên, GPCoderTopicExchange là Source Exchange.
Một Exchange nhận Message từ một Exchange khác gọi là Destination Exchange. Trong sơ đồ trên, GPCoderHeadersExchange là Destination Exchange.
Ví dụ binding Exchange to Exchange trong RabbitMQ
Một số class của chương trình:
ConnectionManager : hỗ trợ tạo Connection đến RabbitMQ.
ExchangeChannel : class util hỗ trợ tạo Echange, Queue, binding Queue đến Exchange, binding Exchange đến Exchange, publish/ subscribe message, …
Constant : định nghĩa constant chứa các thông tin về tên Exchange, Queue.
HeadersExchangeProducer : để gửi Message đến GPCoderHeadersExchange.
TopicExchangeProducer : để gửi Message đến GPCoderTopicExchange.
HeadersExchangeConsumer : để nhận Message từ Queue được binding đến GPCoderHeadersExchange.
TopicExchangeConsumer : để nhận Message từ Queue được binding đến GPCoderTopicExchange.
App: giả lập việc gửi nhận Message thông qua Topic Exchange của RabbitMQ.
ConnectionManager.java
package com.gpcoder.exchange2exchange;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class ConnectionManager {
private ConnectionManager() {
super();
}
public static Connection createConnection() throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
return factory.newConnection();
}
}
package com.gpcoder.exchange2exchange;
public final class Constant {
// Exchange
public static final String HEADERS_EXCHANGE_NAME = "GPCoderHeadersExchange";
public static final String TOPIC_EXCHANGE_NAME = "GPCoderTopicExchange";
// Queue
public static final String DEV_QUEUE_NAME = "QDeveloper";
public static final String MANAGER_QUEUE_NAME = "QManager";
public static final String PUBLISHED_QUEUE_NAME = "QPublished";
public static final String JAVA_QUEUE_NAME = "QJava";
public static final String ALL_QUEUE_NAME = "QAll";
// Routing key pattern
public static final String JAVA_ROUTING_KEY = "java.*.gpcoder.com";
public static final String GPCODER_ROUTING_KEY = "#.gpcoder.com";
// Message key
public static final String JAVA_CORE_MSG_KEY = "java.core.gpcoder.com";
public static final String JAVA_MSG_KEY = "java.gpcoder.com";
public static final String DESIGN_PATTERN_MSG_KEY = "design-pattern.gpcoder.com";
public static final String NOT_MATCHING_MSG_KEY = "java.collection.gpcoder.com.vn";
private Constant() {
super();
}
}
HeadersExchangeProducer.java
package com.gpcoder.exchange2exchange;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import static com.gpcoder.exchange2exchange.Constant.*;
public class HeadersExchangeProducer {
private ExchangeChannel channel;
public void start() throws IOException, TimeoutException {
// Create connection
Connection connection = ConnectionManager.createConnection();
// Create channel
channel = new ExchangeChannel(connection, HEADERS_EXCHANGE_NAME);
// Create headers exchange
channel.declareExchange(BuiltinExchangeType.HEADERS);
// Create queues
channel.declareQueues(DEV_QUEUE_NAME, MANAGER_QUEUE_NAME, PUBLISHED_QUEUE_NAME);
// Binding queues with headers
Map<String, Object> devGroup = new HashMap<>();
devGroup.put("x-match", "any"); // Match any of the header
devGroup.put("dev", "Developer Channel");
devGroup.put("general", "General Channel");
Map<String, Object> managerGroup = new HashMap<>();
managerGroup.put("x-match", "any"); // Match any of the header
managerGroup.put("dev", "Developer Channel");
managerGroup.put("manager", "Manager Channel");
managerGroup.put("general", "General Channel");
Map<String, Object> publishedGroup = new HashMap<>();
publishedGroup.put("x-match", "all"); // Match all of the header
publishedGroup.put("general", "General Channel");
publishedGroup.put("access", "publish");
channel.performQueueBinding(DEV_QUEUE_NAME, "", devGroup);
channel.performQueueBinding(MANAGER_QUEUE_NAME, "", managerGroup);
channel.performQueueBinding(PUBLISHED_QUEUE_NAME, "", publishedGroup);
}
}
package com.gpcoder.exchange2exchange;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import static com.gpcoder.exchange2exchange.Constant.DESIGN_PATTERN_MSG_KEY;
public class App {
public static void main(String[] args) throws IOException, TimeoutException {
HeadersExchangeProducer producer1 = new HeadersExchangeProducer();
producer1.start();
TopicExchangeProducer producer2 = new TopicExchangeProducer();
producer2.start();
// Publish some messages
Map<String, Object> devHeader = new HashMap<>();
devHeader.put("dev", "Developer Channel");
producer2.send("[1] Head First Design Pattern", DESIGN_PATTERN_MSG_KEY, devHeader);
HeadersExchangeConsumer consumer1 = new HeadersExchangeConsumer();
consumer1.start();
consumer1.subscribe();
TopicExchangeConsumer consumer2 = new TopicExchangeConsumer();
consumer2.start();
consumer2.subscribe();
}
}
Output của chương trình:
[Send] [{dev=Developer Channel}]: [1] Head First Design Pattern
[Received] [QDeveloper]: amq.ctag-QVREG0uBg6XsIebvbecqCg
[Received] [QDeveloper]: [1] Head First Design Pattern
[Received] [QManager]: amq.ctag-vLHleyPNedl2ZiMYukIing
[Received] [QManager]: [1] Head First Design Pattern
[Received] [QAll]: amq.ctag-GymeB0uwpALjgoM3RP_cvg
[Received] [QAll]: [1] Head First Design Pattern
Như bạn thấy, Message được gởi tới Exchange với routing key là design-pattern.gpcoder.com và header là {“dev”, “Developer Channel”} sẽ được chuyển tới Queue QAll và GPCoderHeadersExchange. Sau đó GPCoderHeadersExchange sẽ chuyển đến Queue QDeveloper và QManager dựa vào header {“dev”, “Developer Channel”}.
Tóm lại, Exchange Binding trong RabbitMQ là một tính năng vô cùng mạnh mẽ, giúp chúng ta kết hợp nhiều loại Exchange lại với nhau, giúp hệ thống mở rộng tốt hơn và đáp ứng được hầu hết yêu cầu phức tạp của hệ thống.
p5.js là một thư viện Javascript, thường thì nó sẽ sử dụng để dùng làm những thứ linh tinh với đồ họa các thứ sử dụng canvas. Các bạn có thể sử dụng p5.js bằng p5 Web Editor ở đây.
Ở trong p5.js thì sẽ có hai function quan trọng. Đó là setup() và draw().
setup()
Đây là function sẽ chạy ngay lập tức khi chạy. Thường thì hay dùng để config trước khi chạy thực tế.
draw()
Function này sẽ chạy ngay sau thằng setup() ở trên. Đây là function chính của p5.js.
ml5.js là gì?
ml5.js là một thư viện bao gồm các thuật toán và pre-trained models cho browser. ml5.js được build trên nền của tensoeflow.js. Vì thế, mình có thể dùng ml5.js để build một số thứ hay ho dựa trên các pre-trained models có sẵn.
Lý do mình dùng p5.js trong bài này là vì thằng ml5.js này chơi thân với thằng p5.js nên nó dễ sử dụng hơn.
Ví dụ
Bây giờ thì mình làm linh tinh một cái gì đó để ví dụ về cái ml5.js này cho vui. Mình ở đây sẽ làm một cái project vui vui sử dụng poseNet pre-trained model.
poseNet pre-trained model
poseNet pre-trained model là một pre-trained model dùng để mô phỏng vị trí tay, chân, mắt, mũi, miệng, bla bla bla của con người.
Cấu trúc Project
Về cấu trúc thì đơn giản như sau.
index.html
nancy.mp4
script.js
Bash
Trong file index.html thì import p5.js và ml5.js vào thôi
<!DOCTYPE html><htmllang="en"><head><title>Getting Started with ml5.js</title><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.min.js"></script><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/addons/p5.dom.min.js"></script><scriptsrc="https://unpkg.com/ml5@0.4.1/dist/ml5.min.js"></script></head><body><scriptsrc="script.js"></script></body></html>
HTML
Load Video bằng function createVideo của p5.js
Để load Video thì trong p5.js mình dùng function createVideo(). Vì là load video thì mình viết nó trong setup(), sau khi load xong rồi thì mới hiển thị đó ra ở draw() function.
let video;let poses =[];functionsetup(){createCanvas(406,720);
video =createVideo(["nancy.mp4"],()=>{
video.loop();
video.volume(0);});
video.size(width, height);
video.hide();}functiondraw(){image(video,0,0, width, height);}
JavaScript
Lúc này kết quả như sau.
Load poseNet pre-trained model
Công việc tiếp theo là mình load tiếp pre-trained model vào. Mình sẽ viết tiếp trong function setup() như sau.
poseNet = ml5.poseNet(video,()=>{console.log("Model is ready");});// Listen to new 'pose' events
poseNet.on("pose",function(results){
poses = results;});
JavaScript
Vẽ skeleton lên Video
Sau khi lắng nghe sự kiện pose ở trên. Nó sẽ trả một mảng các pose, bao gồm các properties như sau:
// A function to draw the skeletonsfunctiondrawSkeleton(){// Loop through all the skeletons detectedfor(let i =0; i < poses.length; i++){let skeleton = poses[i].skeleton;// For every skeleton, loop through all body connectionsfor(let j =0; j < skeleton.length; j++){let partA = skeleton[j][0];let partB = skeleton[j][1];stroke(255,0,0);strokeWeight(4);line(
partA.position.x,
partA.position.y,
partB.position.x,
partB.position.y
);}}}
JavaScript
Sau đó để function drawSkeleton() này vào function draw()
Trên đây là một ứng dụng cơ bản của thằng ml5.js. Ngoài poseNet pre-trained model, thằng ml5.js còn cung cấp một số pre-trained model khác cũng hay ho, ví dụ như là Image Classifier sử dụng Mobilet Net, FaceApi, hay YOLO. Các bạn cứ ghé vào trang chủ của ml5.js để ngó qua nhé.
Bài viết được sự cho phép của tác giả Trần Khôi Nguyên Hoàng
Ngày nay, xử lý dữ liệu lớn đã trở thành bài toán tiếp xúc hằng ngày đối với Kỹ sư phần mềm, Distributed Data Processing dựa trên MapReduce mà một kỹ thuật được sử dụng để giải quyết những bài toán đó.
Thông qua kiến trúc và cách xử lý của MapReduce. Dữ liệu lên tới hàng Petabyte cũng có thể xử lý linh hoạt và nhanh chóng.
Còn chần chừ gì nữa mà không tìm hiểu xem ta có thể thực hiện như thế nào?. Bắt dầu thôi!
1. Distributed Data Processing
Processing đối với data được đề cập trong bài viết này là đề cập tới dữ liệu lớn (large data). Con số lên tới petabytes.
Ngoài ra, process cũng bao gồm các vấn đề khó nhắn như: Search LIKE, trả về tất cả dữ liệu có chứa dòng chữ “hello”. Sẽ không có gì để bàn nếu ta đang muốn giữ cho peformance ở mức tốt cho dù dữ liệu rất lớn. Bằng cách sử dụng hệ cơ sở dữ liệu phân tán (Distributed Database) kết hợp với kiến trúc MapReduce
Bắt đầu với MapReduce, vậy MapReduce là gì?
1.1 MapReduce = Map + Reduce.
Map, written by the user, takes an input pair and produces a set of intermediate key/value pairs. The MapReduce library groups together all intermediate values associated with the same intermediate key I and passes them to the Reduce function
Map, thường được viết bởi user, nhận đầu vào là một cặp key value, tạo ra một tập hợp các key-value trun gian. Thư viện MapReduce sẽ gom tất cả các cặp key value trung gian cùng với khóa tạm I và gửi chúng tới Reduce function
Túm váy lại thì Map sẽ tạo ra các cặp key-value, nó là input cho Reduce, khi đã có danh sách key-value trung gian, ta sẽ gọi Reduce function.
The Reduce function, also written by the user, accepts an intermediate key I and a set of values for that key. It merges together these values to form a possibly smaller set of values. The intermediate values are supplied to the user’s reduce function via an iterator. This allows us to handle lists of values that are too large totinmemory
Reduce function, cũng được viết bởi user, nhận vào khóa tạm I và danh sách giá trị cho khóa đó. Merge chúng lại với nhau để tạo thành một tập giá trị có thể nhỏ hơn. Các giá trị trung gian được tạo ra thông qua quá trình lặp (interator). Điều này cho phép chúng ta xử lý danh sách các giá trị quá lớn để vừa trong bộ nhớ
Đọc hơi khó hiểu, tuy nhiên chỉ cần nhớ đơn giản rằng MapReduce cho phép lặp các giá trị tuần tự trong MapReduce, đem ra xử lí. Quá trình xử lí này là tuần tự cho từng phần tử. Trường hợp dữ liệu cực lớn, không cần phải có toàn bộ dữ liệu mới thực hiện xử lý (data processing)
2. MapReduce Architecture
Với lượng dữ liệu khổng lồ, nếu muốn xử lí dữ liệu theo hướng thông thường như lưu trên disk, ram, thực hiện tuần tự là điều không khả thi. Chính vì vậy, ta cần một kiến trúc mới, xử lí tuần tự như MapReduce. Lúc này Distributed Data Processing cho dữ liệu lớn trở nên khả thi hơn bao giờ hết.
Bài toán thực tế cho thấy ta có:
Documents: file cần xử lý với dung lượng khổng lồ, cỡ Petabyte
Master: thực hiện tính toán phân chia cho các worker trên system. Dựa vào tài nguyên thì mỗi worker sẽ có khả năng xử lý nhất định, (MB/s)
Worker: Xử lí nội dung file theo yêu cầu từ phía master, sức mạnh của các worker là ngang bằng nhau.
Áp dụng nguyên lý và ý tưởng từ Distributed Storage và Distributed Database, ta sẽ chia Documents thành các phần nhỏ. Mỗi phần sẽ vừa đủ để một Worker có thể xử lí
Trường hợp mỗi Worker có thể xử lý 256MB, ta sẽ chia document thành 4000 phần nhỏ. Nếu có 1000 workers, mỗi worker sẽ chịu trách nhiệm xử lý cho 4 Splits.
Lúc này, vai trò của master trở thành người giám sát, ghi lại tiến trình của từng worker. Phân phối Splits nếu worker đã không còn gì để mà xúc.
Để đảm bảo tính phân tán và stateless của mỗi worker, splits không phân chia tuần tự tới từng worker
Các splits không phân chia tuần tự tới từng worker
3. Handle khi MapReduce fail
Trong quá trình processing data, không thể tránh khỏi các trường hợp bị fail. Lúc này, khi một worker bị fail, master sẽ ghi nhận và đổi trạng thái của worker thành failed
Any map task or reduce task in progress on a failed worker is also reset to idle and becomes eligible for rescheduling
Bất kì Map task hay reduce task đang có trạng thái in progress hay failed đều sẽ reset về trạng thái idle và có thể rescheduling lại
Về phía master
If the master task dies,a new copy can be started from the last check pointed state. However, given that there is only a single master, its failure is unlikely ;therefore our current implementation aborts the MapReduce computation if the master fails. Client scan check for this condition and retry the MapReduce operation if they desir
Nếu master tiêu, một bản copy khác có thể bắt đầu lại từ trạng thái kiểm tra cuối cùng. Tuy nhiên, do chỉ có một master duy nhất, nên việc thất bại ở master thường không xảy ra. Do đó, trường hợp master fail, ta đơn giản hủy bỏ tính toán của MapReduce hiện tại, phía client có thể kiểm tra điều kiện này và thử lại ở MapReduce nào bị fail
React Props Cheatsheet: 10 Patterns mà bạn nên biết (Phần 1)
Tác giả: Reed Barger
Props là công cụ mạnh mẽ được sử dụng trong React, nhưng làm thế nào để bạn sử dụng chúng một cách hiệu quả để viết các thành phần và ứng dụng React mạnh mẽ, đáng tin cậy?
1. React props có thể pass một cách có điều kiện
Các đạo cụ được chuyển cho các thành phần có thể được coi như các đối số được truyền cho một hàm. Nếu các giá trị prop không được thông qua một thành phần nhất định, error sẽ không xuất hiện. Thay vào đó, trong thành phần prop sẽ có giá trị là undefined.
Nếu bạn muốn được cảnh báo về thời điểm một giá trị không được chuyển làm hỗ trợ cho một thành phần, bạn có thể sử dụng một công cụ như prop-type hoặc TypeScript bằng các công cụ này.
Trong React đơn giản, hãy lưu ý rằng bạn thường quên việc chuyển các đạo cụ. Ngoài ra, bạn có thể không chuyển một chỗ dựa nhất định cho một thành phần, nếu bạn chọn.
2. React props được thông qua khi tên chúng có giá trị đúng
Mỗi prop phải được cung cấp một giá trị liên quan được cung cấp sau toán tử bằng. Nhưng điều gì sẽ xảy ra khi chúng ta không cung cấp toán tử bằng cũng như một giá trị?
Nếu bạn chỉ cung cấp tên riêng cho một thành phần mà không có gì khác, bạn sẽ chuyển một giá trị boolean là true cho phần hỗ trợ đó cho thành phần đó. Không cần phải viết rằng một prop bằng true. Thay vào đó, bạn chỉ có thể bao gồm giá trị prop và nó sẽ được cung cấp giá trị boolean true khi bạn sử dụng nó trong một thành phần mà bạn chuyển nó vào.
3. React props có thể truy cập như một object hoặc tái cấu trúc
Có một số mẫu mà chúng ta có thể sử dụng để truy cập các giá trị prop trong các thành phần của chúng ta. Props có thể được truy cập dưới dạng toàn bộ đối tượng thường được gọi là “props”. Hoặc chúng có thể tái cấu trúc, vì đạo cụ sẽ luôn là một đối tượng, thành các biến riêng biệt. Nếu bạn có nhiều đạo cụ đang chuyển cho thành phần của mình, tốt nhất có thể đưa chúng vào toàn bộ đối tượng đạo cụ và truy cập chúng bằng cách nói props.propName.
Tuy nhiên, nếu bạn chỉ có một vài đạo cụ mà bạn đang chuyển xuống thành phần của mình, bạn có thể hủy cấu trúc chúng ngay lập tức trong các tham số của thành phần chức năng của bạn.
Bạn có thể sử dụng cấu trúc đối tượng ES6 để bao gồm một tập hợp các dấu ngoặc nhọn trong các tham số của thành phần hàm của bạn và ngay lập tức lấy ra các thuộc tính của đối tượng, cũng như khai báo chúng dưới dạng các biến riêng biệt. Điều này cắt giảm code của chúng tôi và loại bỏ sự cần thiết phải nói props.propName để nhận từng giá trị đạo cụ.
Chúng tôi đã thấy rằng các đạo cụ rất linh hoạt và nếu chúng tôi không chuyển chúng cho một thành phần, error sẽ không xuất hiện. Tính linh hoạt này cũng được mở rộng cho những gì chúng ta có thể vượt qua như một chỗ dựa. Không chỉ các phần tử JSX có thể được chuyển làm đạo cụ cho các thành phần mà chúng ta còn có thể chuyển các thành phần khác làm đạo cụ.
Trên thực tế, có một loại giá đỡ đặc biệt được cung cấp tự động trên đối tượng đạo cụ được gọi là children.
5. Mọi thứ có thể được thông qua như prop trong React
Bất kỳ giá trị JavaScript bình thường nào cũng có thể được chuyển dưới dạng props, bao gồm cả các hàm. Có một số mẫu mạnh mẽ đã xuất hiện, do khả năng chuyển các chức năng làm đạo cụ. Một mẫu rất phổ biến là truyền một hàm xuống một thành phần con như một chỗ dựa, có thể cập nhật trạng thái của thành phần mẹ, và sau đó gọi nó trong thành phần con đó.
Ngoài ra, còn có các mẫu khác, chẳng hạn như mẫu đạo cụ kết xuất, cũng liên quan đến việc chuyển một hàm xuống thành phần con để sau đó được gọi lại và thực hiện một số chức năng thành phần chéo.
Thay vì tạo mỗi observable để chờ đợi các subscribers đăng ký, chúng ta có thể tạo 1 factory observable sinh ra các observable cho mỗi lượt đăng ký mới của chúng.
Tạo 1 factory observable kiểu nguyên bằng toán tử deferred
Nghịch đảo flip, mỗi khi factory observable được đăng ký
Trả về các Observale khác nhau tùy thuộc giá trị của flip
Thêm mã sau vào sau hàm trên:
for _ in 0...3 {
factory.subscribe(onNext: {
print($0, terminator: "")
})
.disposed(by: disposeBag)
print()
}
Kết quả như sau:
--- Example of: deferred ---
123
456
123
456
Vậy ta hiểu là, mỗi lần khi ta đăng ký tới factory observable, thì ta nhận các giá trị khác nhau của chúng theo thứ tự xen kẽ nhau. Điều này để thể hiện rằng factory observable có thể tạo ra các observable khác nhau cho các subscriber.
Vậy là bài hôm nay kết thúc rồi. Trong bài tiếp theo chúng ta sẽ tập trung thực hành các khái niệm chính của Rxswift. Cụ thể bài tới là về Subjects.
Windows có lẽ là một hệ điều hành đã quá quen thuộc với người dùng phổ thông rồi, và macOS cũng vậy (vì đã xài Macbook thì mặc định sẽ là macOS mà – tuy nhiên mức độ phổ biến thì không thể bằng Windows được.
Còn với Linux và các phiên bản phát triển từ nhân Linux (như Ubuntu, Kali Linux, Pop!_OS…) thì không phải ai cũng lựa chọn để dùng và biết cách để dùng.
Vậy tại sao mình lại nói các bạn lập trình viên nên sử hệ điều hành này? Vâng, ở trong bài viết này mình sẽ chỉ ra cho bạn 5 lý do mà mình thấy là hợp lý nhất để các bạn chuyển sang sử dụng nền tảng này, các bạn có thể bổ sung thêm dưới phần comment về góc nhìn của bạn nhé
#1. Không phù hợp để chơi game !
Mình tin chắc là sẽ có nhiều bạn nghĩ lý do này là không thuyết phục, vì chuyện chơi game sẽ phụ thuộc vào sở thích cũng như lý trí mỗi người.
Mình không hề phủ nhận điều đó, nhưng một trong những cách để bạn tập trung hơn vào công việc đó là đừng bắt bản thân phải đưa ra lựa chọn. OK !
Ví dụ như khi bạn bạn đang xài hệ điều hành Windows và máy bạn có cài sẵn một số tựa game. Điều này khiến bạn nhiều lúc phải đưa ra lựa chọn giữa làm việc và chơi game để giải trí một lúc.
Tất nhiên rồi, sẽ có lúc bạn không thể cưỡng lại được sự lôi cuốn của mấy con game mình thích, và thế là bạn lại lao vào chơi game mà quên béng đi mất mình đang phải làm việc.
Mình đã từng trong tình trạng này rồi nên mình hiểu rất rõ cảm giác việc phải đưa ra lựa chọn như thế. Về lâu về dài nó sẽ không tốt cho việc hình thành thói quen của bạn.
Đấy là với các bạn làm chủ bản thân kém, còn với các bạn học ra học, chơi ra chơi thì đây có lẽ không phải là một lý do thuyết phục thật.
#2. Linux hoàn toàn miễn phí và Open Source
Windows hay Mac OS đều là những hệ điều trả phí. Và tất nhiên, bạn sẽ phải trả một khoản tiền không hề nhỏ so với thu nhập bình quân đầu người ở Việt Nam để có thể sở hữu bản quyền CHÍNH THỨC.
Mặc dù ở Việt Nam vấn đề bản quyền chưa được đề cao nên số lượng người sử dụng bản quyền lậu vẫn rất nhiều, đặc biệt là với hệ điều hành Windows. Nhưng mình thấy mấy năm trở lại đây chúng ta đã đỡ hơn rất nhiều rồi.
Còn ở nước ngoài thì khác, nơi mà vấn đề bản quyền được đề cao thì việc dùng lậu sẽ mang đến rất nhiều rủi ro cho người dùng. Chúng ta dần dần cũng vậy thôi, chắc chắn là như vậy !
Trong khi đó, lập trình viên lại là những người làm việc thường xuyên với máy tính và hệ điều hành. Ai sẽ là người chịu trách nhiệm khi mà hệ điều hành bạn dùng bị lỗi trong khi bạn đang dùng các phiên bản c.r.a.c.k, các phiên bản bẻ khóa…
Đây chính là lý do tại sao khi bạn hỏi các lập trình viên ở các nước phương tây, họ dùng Linux rất nhiều !
Linux là hệ điều hành mã nguồn mở, hoàn toàn miễn phí và open source. Có nghĩa là bạn không cần phải trả phí để mua bản quyền, mà chỉ cần tải về, cài đặt và dùng thôi.
Vậy một câu hỏi đặt ra là nhỡ có bị lỗi thì ai là người đứng ra chịu trách nhiệm? Vâng, thực ra là Linux được cả cộng đồng xây dựng chung nên rất ít lỗi và nếu có lỗi thì cộng đồng Linux cũng rất đông và sẽ giúp đỡ bạn thôi.
#3. Linux hỗ trợ hầu hết các ngôn ngữ lập trình
Đã là lập trình viên thì phần lớn thời gian họ làm việc với các ngôn ngữ lập trình, mà Linux và các phiên bản của Linux thì lại hỗ trợ hầu hết các ngôn ngữ lập trình: C/C++, Java, Python, PHP…
Không những vậy, việc thao tác dòng lệnh (command line) trên Linux phải gọi là rất sướng, sướng hơn Windows rất nhiều. Các bạn có thể cài đặt mọi thứ, từ ngôn ngữ lập trình cho đến các IDE, tools bằng cách gõ các dòng lệnh. Cảm giác mình sử dụng máy tính ở một cái tầm khác.
Nhiều bạn lập trình viên có tần suất dùng chuột rất ít do là đã quá quen với việc thao tác với dòng lệnh rồi. Tất nhiên khi bạn dùng quen rồi thì bạn cũng có khả năng làm được như vậy thôi.
Hơn nữa, hiện nay hầu hết các ngôn ngữ lập trình đều hỗ trợ CLI (Command Line Interface) nên khi bạn dùng Linux, bạn chỉ cần gõ lệnh là hầu như làm được hết mọi thứ.
#4. Tính bảo mật cao
Trước khi nói về tính bảo mật của Linux thì mình sẽ nói về tính bảo mật của Windows trước. Các bạn vẫn thường nghe về các lần vá lỗi bảo mật hoặc là thông báo lỗ hổng bảo mật trên Windows rất nhiều, đúng chứ.
Đó là khi bạn dùng bản Windows sạch, còn nếu bạn dùng các phiên bản c.r.ac.k, phiên bản lậu thì nguy cơ bị đe dọa bảo mật lại càng nghiêm trọng hơn nữa. Nhiều bạn cẩn thận, máy có nhiều tài liệu quan trọng sẽ còn phát sinh thêm chi phí mua thêm các phần mềm diệt virus.
Nhưng trên Linux thì không, Linux được cả cộng đồng đông đảo chung tay phát triển. Tất nhiên, mình không khẳng định Linux không có lỗi, nhưng nếu xét về tính bảo mật và an toàn thì mình vẫn đánh giá cao Linux hơn.
Vậy tại sao Linux lại bảo mật hơn Windows?
+ Quyền “root”: Gọi nôm na là quyền Admin đấy các bạn. Bình thường khi bạn cài Windows thì mặc định bạn sẽ có quyền này, nó cho phép bạn thực hiện mọi thao tác với hệ thống với quyền hạn cao nhất.
Nhưng trên Linux thì không, người dùng không được cấp quyền này theo mặc định. Nói cách khác thì dù có bị virus xâm nhập thì cũng không có quyền root để mà phá hoại hệ thống.
+ Ít bị dòm ngó bởi các hacker: Vâng, dễ hiểu thôi, mình lấy ngay ví dụ bên trên nhé giả dụ như có cài virus vào được rồi mà không làm gì được thì đương nhiên hacker sẽ tìm những nạn nhân khác dễ nuốt hơn. Mà khi ngoài tầm ngắm rồi thì càng ít bị tấn công, vậy nên vấn đề bảo mật càng ít bị đe dọa.
Còn nhiều nguyên nhân khác nữa, nhưng theo mình thấy đây là hai nguyên nhân tiêu biểu cũng như là dễ thấy nhất.
#5. Khả năng tùy biến cực cao
Như mình đã nói ở trên, Linux miễn phí 100%, có nghĩa là bạn có thể tùy chỉnh mọi thứ từ font chữ, theme, icon… bạn có thể tùy biến rất sâu vào hệ thống, tùy theo ý của bạn.
Thậm chí nhiều phiên bản hệ điều hành còn được xây dựng trên nền tảng các hệ điều hành Linux có sẵn. Khi bạn sử dụng Linux nó sẽ đem lại cho bạn một cảm giác gọi là cảm giác “được kiểm soát”.
Nói nôm na là bạn muốn làm gì thì làm, điều này không giống với Windows hay Mac OS vì hai hệ điều hành này còn liên quan tới vấn đề bản quyền nữa.
Mà các bạn lập trình nếu làm việc can thiệp sâu vào hệ thống, tới tầng hệ điều hành thì điều này lại cực kỳ quan trọng hơn nữa, do phải hiểu thì mới làm được, mới xây dựng sản phẩm trên hệ điều hành đó được.
#6. Lời Kết
Như vậy là trong trong bài viết này mình đã cùng với các bạn điểm qua 5 lý do rất thuyết phục để những bạn đang hoặc sẽ là lập trình viên thì nên sử dụng hệ điều hành Linux rồi nhé.
Có thể đúng nhưng chưa đủ, vậy nên rất mong được các bạn đóng góp thêm ý kiến mới, cũng như là đưa ra góc nhìn của bạn về vấn đề này để anh em cùng trao đổi thêm. Cám ơn các bạn trước nhé
Bài viết được sự cho phép của tác giả Trần Hữu Cương
Giới thiệu lớp Activity trong Android
Lớp Activity là thành phần quan trọng nhất của ứng dụng Android, cách mà chúng hoạt động tạo thành nền tảng cơ bản của mô hình lập trình ứng dụng.
Khi một Activity chỉ định là Activity chính, nó sẽ là màn hình đầu tiên khi khởi chạy ứng dụng. Một Activity này lại có thể gọi và kích hoạt một Activity khác.
Activity chịu trách nhiệm chuyển giao sự kiện cho các view trong nó và quản lý vòng đời của nó. Một ứng dụng Android có thể có một hoặc nhiều Activity.
Một class được gọi là Activity khi nó extend (kế thừa) từ những class cha như : AppCompatActivity, Activity, FragmentActivity
Để tạo một Activity thì bạn phải tạo ra một lớp kế thừa lớp Activity, sau đó triển khai tối thiểu phương thức onCreate(Bundle savedInstanceState), sau đó tùy ngữ cảnh mà khi Activity hoạt động vòng đời của nó diễn ra như mô tả ở hình sau:
onCreate(Bundle savedInstanceState): Được gọi khi hoạt động mới được tạo, tại đây khởi tạo các biến, nạp giao diện layout …, phương thức này cũng nhận dữ liệu lưu lại trạng thái hoạt động trước đó (với mục đích để phục hồi – savedInstanceState).
onStart(): Được gọi ngay trước khi Activity hiển thị trên màn hình.
onResume(): Được gọi ngay khi Activity bắt đầu có thể tương tác với người dùng, và Activity nằm trên cùng trong danh sách các Activity của hệ thống.
onPause(): Được gọi khi hệ thống sắp kích hoạt một Activity khác, nếu bạn quá tải phương thức này, thường để lưu lại dữ liệu thật nhanh để hệ thống còn kích hoạt Activity khác.
onStop(): Được gọi khi nó bị ẩn đi. Sau phương thức này, Activity có thể gọi onRestart() nếu nó được người dùng kích hoạt lại hoặc gọi onDestroy() để hết thúc.
onDestroy(): gọi khi Activity bị hủy hoàn toàn (ví dụ gọi finish(), hoặc người dùng kill Activity)
Trong vòng đời của ứng dụng Android bạn cần phân biệt 2 loại sau: Visible Lifetime và Foreground Lifetime
Visible Lifetime: xảy ra từ sau khi gọi onStart –> cho tới lúc gọi onStop : trong trường hợp này ta vẫn có thể thấy màn hình Activity
Foreground Lifetime: xảy ra từ khi gọi onResume –> cho tới lúc gọi onPause : trong suốt thời gian này Activity luôn nằm ở trên cùng và ta có thể tương tác được với nó
Bài viết được sự cho phép của tác giả Phạm Công Sơn
Sắp xếp chèn (insertion sort) là một thuật toán sắp xếp bắt chước cách sắp xếp quân bài của những người chơi bài. Muốn sắp một bộ bài theo trật tự người chơi bài rút lần lượt từ quân thứ 2, so với các quân đứng trước nó để chèn vào vị trí thích hợp.
var InsertionSort = function ()
{
$.extend(this, new SortX());
this.onCreateScripts = function (array, scripts)
{
var $this = this;
var addScript = function (a1, a2, action)
{
scripts.push($this.createScript0(a1, a2)); // Script lấy ra 2 số cần so sánh
scripts.push($this.createScript1(a1, a2)); // Script để thực hiện so sánh giữa 2 số
return action();
}
var j, last;
for (var i = 1; i < array.length; i++) { last = array[i]; j = i; while ((j > 0) && addScript(array[j - 1], last, () => array[j - 1].number > last.number))
{
var a1 = array[j - 1];
array[j] = a1;
scripts.push($this.createScript2(a1, last));
j = j - 1;
}
array[j] = last;
}
}
}
var pageSortX = new PageSortX();
pageSortX.sortX = new InsertionSort();
pageSortX.start();
Bài viết được sự cho phép của tác giả Phạm Công Sơn
Chọn phần tử nhỏ nhất trong n phần tử ban đầu, đưa phần tử này về vị trí đúng là đầu tiên của dãy hiện hành. Sau đó không quan tâm đến nó nữa, xem dãy hiện hành chỉ còn n-1 phần tử của dãy ban đầu, bắt đầu từ vị trí thứ 2. Lặp lại quá trình trên cho dãy hiện hành đến khi dãy hiện hành chỉ còn một phần tử. Dãy ban đầu có n phần tử, vậy tóm tắt ý tưởng thuật toán là thực hiện n-1 lượt việc đưa phần tử nhỏ nhất trong dãy hiện hành về vị trí đúng ở đầu dãy.
var SelectionSort = function ()
{
$.extend(this, new SortX());
this.name = "SelectionSort";
this.onCreateScripts = function (array, scripts)
{
var $this = this;
var addScript = function (a1, a2, action)
{
scripts.push($this.createScript0(a1, a2)); // Script lấy ra 2 số cần so sánh
scripts.push($this.createScript1(a1, a2)); // Script để thực hiện so sánh giữa 2 số
return action();
}
var min;
for (var i = 0; i < array.length - 1; i++)
{
min = i;
scripts.push(this.createScriptOneItem(array[min]));
for (var j = i + 1; j < array.length; j++) { if (addScript(array[j], array[min], () => array[j].number < array[min].number))
{
min = j;
scripts.push(this.createScriptOneItem(array[min]));
}
}
var b1 = array[i];
var b2 = array[min];
array[min] = b1;
array[i] = b2;
scripts.push(this.createScript2(b1, b2)); // Script thực hiện hoán đổi giữa 2 số với nhau.
}
}
}
var pageSortX = new PageSortX();
pageSortX.sortX = new SelectionSort();
pageSortX.start();
Như vậy là mình đã giới thiệu cho các bạn seri hướng dẫn lập trình theme woocommerce. Seri này mình đã dự định làm cách đây 1 năm trước, nhưng bây giờ mới có thời gian để thực hiện.
Hy vọng với seri này các bạn sẽ biết được cách xây dựng 1 website bán hàng chuyên nghiệp với wordpress.
Bài viết được sự cho phép của tác giả Lê Xuân Quỳnh
Trong bài 9, chúng ta đã tìm hiểu qua cách hoạt động của các loại subjects. Trong bài này, chúng ta sẽ nghiên cứu cách sử dụng các loại subjects trông qua source code cụ thể.
Ở hình trên, chúng ta có 3 đường thẳng thể hiện cho 3 subscribes đăng ký các sự kiện trong thời gian thực. Trong đoạn code bài trước, đường thẳng đầu tiên thể hiện subscribe 1. Sau khi đăng ký, Publish subjects emit 1 sự kiện string là “1”. Lúc đó subscriptionOne nhận được.
Chúng ta thêm đoạn code sau để đăng ký tiếp subscriptionTwo:
example(of: "PublishSubject") {
let subject = PublishSubject<String>()
subject.onNext("Co ai nghe tui noi khong?")
let subscriptionOne = subject
.subscribe(onNext: { string in
print(string)
})
subject.on(.next("1"))
// Add subscription two
let subscriptionTwo = subject
.subscribe { event in
print("2)", event.element ?? event)
}
}
Kết quả chạy PlayGround như sau:
--- Example of: PublishSubject ---
1
Ở đây chúng ta thấy mặc dù đã đăng ký nhưng subscriptionTwo không hề emit ra sự kiện nào. Điều này dễ dàng thấy rằng, 1 PublishSubject sẽ chỉ thực sự phát ra sự kiện cho subscriptionTwo sau khi nó đã đăng ký.
Tiếp tục thêm đoạn code sau:
subscriptionOne.dispose()
subject.onNext("4")
Kết quả chạy chương trình:
--- Example of: PublishSubject ---
1
2) 4
Lúc này subscriptionOne đã dispose, cho nên nó không thể tiếp tục nhận sự kiện từ PublishSubject.
Đầu tiên gửi sự kiện hoàn thành completed cho subject
Gửi 1 sự kiện “5”. Lúc này không có sự kiện nào được in ra, bởi vì subject đã thực sự kết thúc.
dispose subscriptionTwo báo cho nó biết là đã kết thúc việc lắng nghe
Tạo 1 subscription cho subject. Thêm disposeBag cho nó.
Gửi tiếp sự kiện “?”. Thì subject báo nó đã hoàn thành, nên nó không hiển thị thêm “?” nữa. Tuy nhiên subscriptionOne, subscriptionTwo vẫn hiển thị sự kiện đã hoàn thành mặc dù nó đã dispose. Do vậy, bạn phải chú ý khi sử dụng PublishSubject khi xử lý.
Source đầy đủ:
import UIKit
import RxSwift
public func example(of description: String, action: () -> Void) {
print("\n--- Example of:", description, "---")
action()
}
example(of: "PublishSubject") {
let subject = PublishSubject<String>()
subject.onNext("Co ai nghe tui noi khong?")
let subscriptionOne = subject
.subscribe(onNext: { string in
print("1)" + string)
})
subject.on(.next("1"))
// Add subscription two
let subscriptionTwo = subject
.subscribe { event in
print("2)", event.element ?? event)
}
subscriptionOne.dispose()
subject.onNext("4")
// 1
subject.onCompleted()
// 2
subject.onNext("5")
// 3
subscriptionTwo.dispose()
let disposeBag = DisposeBag()
// 4
subject
.subscribe {
print("3)", $0.element ?? $0)
}.disposed(by: disposeBag)
subject.onNext("?")
}
Đôi khi bạn muốn khi 1 subscription đăng ký mới muốn biết các event trước đó là gì. Chúng ta sẽ cùng nghiên cứu trong bài 11 về BehaviorSubjects.
Khi bạn truy cập trang web trên, thì trình duyệt sẽ tự động download file .exe cài đặt. Tuy nhiên bạn cũng có thể download các phiên bản khác như bản 32bit/64bit hay bản portable
Phần Editor mặc định cho Git (phần mềm sử dụng để hiển thị, chỉnh sửa file khi compare file với Git). Mình sử dụng notepad++ vì nó khá dễ nhìn và nhẹ (các bạn có thể chọn editor khác)
Kiểm tra phiên bản của bản git vừa cài đặt bằng cách mở màn hình cmd hoặc powershell và chạy lệnh
Bức ảnh trên là sự khái quát về một lộ trình phổ biến nhất cho các Web Developer hiện nay trên thị trường web development. Trước khi chúng ta đi sâu vào các kỹ năng bạn cần học để trở thành một nhà phát triển web, bạn nên biết rằng phát triển website không phải là một khái niệm hay một chủ đề duy nhất. Nó có rất nhiều vấn đề phát sinh bên trong đó.
Bạn có thể tập trung vào việc học cách phát triển giao diện người dùng, UX UI, thiết kế đồ họa,… Trước hết, bạn có thể bắt đầu với việc phát triển giao diện người dùng. Để phát triển Front-end, bạn cần học HTML, CSS, JavaScript và thư viện hoặc khuôn khổ như React, Angular hoặc Vue.js.
Vì vậy, bây giờ chúng ta hãy nói chi tiết hơn về phát triển giao diện người dùng là gì và những gì bạn cần học.
“Giao diện người dùng của một ứng dụng thường đề cập đến lớp đại diện cho giao diện người dùng (giao diện người dùng). Điều này có thể bao gồm mọi thứ từ một trang web tĩnh với HTML và CSS đến một ứng dụng React đầy đủ hỗ trợ giao diện người dùng” – Colby Fayock.
Có một số kiến thức cơ bản bạn cần biết để phát triển Web Frontend bao gồm: HTML, CSS, JavaScript. Trong đó, HTML và CSS sử dụng để tạo các trang web tĩnh.
Back End Development là gì?
Phát triển back end còn được gọi là phát triển phía máy chủ. Đó là thực hành giao tiếp giữa cơ sở dữ liệu và trình duyệt. Các ngôn ngữ và công cụ của Back-end Development hiện có bao gồm: Node.js, Express, MongoDB và Mongoose.
Những nguồn tài nguyên miễn phí để học cách phát triển website
freeCodeCamp
Đây là trang web quan trọng nhất mà tôi từng truy cập. Họ có một giáo trình về web development đầy đủ kéo dài 3000 giờ và gần đây họ cũng đã thêm các chứng chỉ về tính toán khoa học, phân tích dữ liệu và học máy với Python.
Ngoài chương trình học, freeCodeCamp có một ấn phẩm (nơi bạn đang đọc bài viết này), kênh YouTube và Diễn đàn nơi bạn có thể nhận trợ giúp về các vấn đề lập trình của mình.
Bạn cũng có thể đăng ký với tư cách là người đăng ký và Quincy (người sáng lập freeCodeCamp) sẽ gửi email cho bạn một số bài báo liên quan đến công nghệ mới nhất từ ấn phẩm của freeCodeCamp hàng tuần.
Udemy
Chỉ cần tìm kiếm free courses from Udemy và nó sẽ lọc ra các tùy chọn của bạn. Bạn sẽ thấy cả khóa học trả phí và miễn phí, nhưng bạn chỉ có thể chọn những khóa học miễn phí và tận hưởng.
Nếu bạn còn nhớ, khóa học lập trình đầu tiên của tôi là từ Udemy – khóa học mà bạn tôi đã đăng ký cho tôi. Vì vậy, nó đã giúp tôi bắt đầu.
Tài liệu tham khảo
Khi tôi muốn tìm hiểu hoặc triển khai một khái niệm mới, tôi thường truy cập Google. Và Google thường đề xuất MDN hoặc tài liệu chính thức của một công cụ hoặc ngôn ngữ. Điều này cũng hữu ích khi tôi quên cách hoạt động của các chi tiết của một chủ đề cụ thể (như Flexbox chẳng hạn).
W3Schools cung cấp rất nhiều hướng dẫn tham khảo ngắn cho nhiều chủ đề bao gồm Java, Python, JavaScript, jQuery, React, Angular, AJAX, SQL, Node.js, Raspberry Pi, Artificial Intelligence, Machine Learning, Data Science, NumPy, SciPy, Matplotlib và MongoDB.
Kết luận
Đây chỉ là một hướng dẫn ngắn gọn dành cho các web developer, để giúp bạn bắt đầu trong lĩnh vực web development. Bạn có thể đánh dấu bài viết này để sử dụng thêm hoặc thậm chí chia sẻ nó với bạn bè của bạn, những người cũng muốn bắt đầu sự nghiệp của họ với tư cách là Nhà phát triển web.
Bài viết được sự cho phép của tác giả Nguyễn Việt Hưng
I. Memoization
Memoization không phải là một từ Tiếng Anh có thể tìm thấy trong từ điển Oxford Online . Nó là biến thể của từ gốc Latin “memoradum” với nghĩa “to be remembered” (được nhớ).
Trong lập trình, memoization là một kỹ thuật tối ưu, nhằm tăng tốc chương trình bằng cách lưu trữ kết quả của các câu gọi function và trả về các kết quả này khi function được gọi với cùng input đã gọi.
Hiểu đơn giản, trong python ta có thể implement memoization với dict bằng cách lưu kết quả gọi function f vào một dict theo dạng:
và sửa lại function để nó lấy result1 nếu input1 có trong dict.
Với bài toán tìm số Fibonacci, kết quả của câu gọi function sau bằng tổng kết quả của 2 lần gọi function liền trước, dễ thấy ta có thể sử dụng memoization để tránh việc tính lại (đồng thời tránh luôn cả việc recursive call quá nhiều khiến vượt quá kích thước của stack)
def fib(n):
if n <= 2: return 1
else:
return fib(n - 1) + fib(n - 2)
In [9]: fib(6)
Out[9]: 8
In [12]: fib(25)
Out[12]: 75025
In [13]: fib(75)
... chờ mãi không thấy
In [13]: %time fib(30)
CPU times: user 244 ms, sys: 1.83 ms, total: 246 ms
Wall time: 245 ms
Out[13]: 832040
Nếu dùng memoization để tối ưu việc tính số Fibonacci , ta không phải tính lại các giá trị đã tính rồi:
LRU (least recently used) cache (đọc là /kaʃ/) là một trong các thuật toán cache phổ biến. Cache được dùng để lưu trữ các kết quả tính toán vào một nơi và khi cần tính lại thì lấy trực tiếp kết quả đã lưu ra thay vì thực hiện tính. Cache thường có kích thước nhất định và khi đầy, cần bỏ đi một số kết quả đã tồn tại trong cache. Việc kết quả nào sẽ bị bỏ đi phân loại các thuật toán cache này thành:
LRU (Least Recently Used): bỏ đi các item trong cache ít được dùng gần đây nhất.
MRU (Most Recently Used): bỏ đi các item trong cache được dùng gần đây nhất.
Việc sử dụng kỹ thuật memoization để tối ưu các quá trình tính toán như vậy là chuyện thường ở huyện, vậy nên từ Python 3.2, trong standard library functools đã có sẵn function lru_cache giúp thực hiện công việc này ở dạng decorator. Khi gọi function với một bộ argument, lru_cache sẽ lưu các argument lại thành key của dict, và sử dụng kết quả gọi function làm value tương ứng. lru_cache có option để chỉnh kích thước của cache, phân biệt kiểu của argument.
Một điểm đáng chú ý là các argument được gọi với function đều phải là immutable object bởi chúng được dùng làm key của dict. Khi dùng decorator lru_cache, ta chỉ cần tập trung vào viết function cần viết, lru_cache sẽ lo việc thực hiện caching/memoization.
In [8]: @lru_cache(maxsize=None)
def fib(n):
if n <= 2: return 1
else:
return fib(n-1) + fib(n-2)
...:
In [9]: %time fib(75)
CPU times: user 84 µs, sys: 26 µs, total: 110 µs
Wall time: 114 µs
Out[9]: 2111485077978050
Ở đây bạn nên tải phiên bản with VirtuaBox nhé, nó tích hợp sẵn VirtuaBox cho các bạn vì máy ảo phải có VirtuaBox mới chạy được. (Nếu bạn đã cài VirtuaBox thì có thể bỏ qua)
Sau khi download về các bạn hãy cài đặt chỉ việc next theo hướng dẫn của nó và khởi động nó lên chúng ta sẽ được giao diện như sau:
Các bạn có thể thêm được rất nhiều máy ảo mà các bạn thích,các bạn chỉ cần nhấn add và tải thêm nhiều mấy ảo để test trên các thiết bị.
Bạn chọn máy ảo mình muốn làm việc và click nút Start ở phía trên để khởi động máy ảo:
Chờ máy ảo khởi động:
Và đây là kết quả
Cài đặt plugin để sử dụng trực tiếp Genymotion trong Android Studio
Bạn vào Android Studio để cài đặt plugin. Click File > Settings > Plugins
Ở cửa sổ mới mở ra các bạn chọn “Plugin” rồi nhấn vào nút “Browse repositories”:
Các bạn gõ Genymotion vào ô tìm kiếm, sẽ thấy nó hiện ra plugin của Genymotion ở phía dưới. Sau đó các bạn click và nút ở Install plugin ở bên cạnh, hộp thoại xác nhận hiện ra thì chọn Yes để Android Studio tiến hành tải và cài đặt plugin:
Sau khi cài đặt xong, bạn sẽ có 1 biểu tượng Genymotion trên thanh tác vụ. Bất cứ khi nào bạn muốn chạy máy ảo Genymotion, chỉ cần chọn một cái trong danh sách và nhấp vào Bắt đầu là được.
Bài viết được sự cho phép của tác giả Trần Thị Thu Hà
SmarJob xin chia sẻ với các bạn kỹ thuật đa ngôn ngữ ứng dụng web dựng trên nền Spring Web MVC. Chúng ta đang sống trong môi trường toàn cầu hóa, ứng dụng muốn tiến xa cần tiếp cận lượng người dùng đông đảo, thuộc nhiều quốc gia khác nhau. Khi đó phát sinh rào cản là ngôn ngữ. Phá vỡ được rào cản ngôn ngữ, ứng dụng bạn tạo ra sẽ nhiều người dùng hơn, doanh thu cao hơn.
Khởi tạo project bằng Maven Archetype maven-archetype-webapp
Chọn kiểu project là Maven, Sử dụng Java 8, Archetype là maven-archetype-webapp
Khai báo 3 thông số GAV (GroupId – ArtifactId – Version)cho ứng dụng sắp tạo ra:
G: vn.smartjob.demo_spring
A: multilanguage
V: 1.0.0–SNAPSHOT (hậu tố –SNAPSHOT được khuyến khích sử dụng)
Màn hình xác nhận ứng dụng Maven sẽ sử dụng, cũng như thông số cấu hình để quản lý dependencies cho project. Nếu bạn nhập sai thông tin, có thể bấm nút Previous để quay lại và sửa.
Đặt tên và chọn vị trí lưu mã nguồn project:
Bạn cần tạo thêm các thư mục và tập tin để cấu trúc cây như sau:
Là project sử dụng Maven build tool, nên việc đầu tiên bạn cần quan tâm là file pom.xml:
Controller điều hướng luồng đi trong ứng dụng Spring Web MVC:
package vn.smartjob.demo_spring.multilanguage;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class WelcomeController {
@RequestMapping(value = "/welcome")
public ModelAndView user() {
return new ModelAndView("welcome");
}
}
Để minh họa, chúng ta hỗ trợ 3 ngôn ngữ: Tiếng Anh, Tiếng Việt, Tiếng Nhật. Bạn có thể hỗ trợ thêm số ngôn ngữ không giới hạn. Điều này khiến lượng khách hàng/lượng người dùng đa quốc gia tăng lên mạnh mẽ:
File messages_en_US.properties
hello=Hello
File messages_vi_VN.properties
hello=Xin chào
File messages_ja_JP.properties
hello=こんにちは
Mẹo: Để đảm bảo font chữ hiển thị đúng, sử dụng Notepad++ biên soạn nội dung đa ngôn ngữ, để chế độ mã hóa (encoding) là UTF-8
Kết quả
Tải về source code từ server SmartJob: multilanguge hoặc clone/fork từ server GitHub: https://github.com/SmartJobVN/spring_mvc_multi_language
Bài viết gốc của Đỗ Như Vý được đăng tải tại smartjob.vn