We discussed a regular expression used to format a number string by adding commas every three digits. The regular expression is (\d)(?=(?:\d{3})+(?!\d)) and it is used with the replace method in JavaScript to add commas to the number string.
Regular Expression Breakdown
We broke down the regular expression into its components:
(\d): This is a capture group that matches any single digit.
(?=...): This is a positive lookahead that matches the following pattern without including it in the match.
(?:...): This is a non-capturing group that groups together the following pattern without creating a capture group.
\d{3}: This matches any three digits.
+: This matches one or more of the previous pattern.
(?!\d): This is a negative lookahead that matches the following pattern only if it is not followed by a digit.
Regular Expression Explanation
We explained that the regular expression matches any single digit that is followed by one or more groups of three digits that are not followed by a digit. This effectively matches every digit in the number string except for the ones at the end that are not part of a group of three.
Example Process
We provided an example process to demonstrate how the regular expression works. Suppose we have the number string “1000000000”. The regular expression matches the first digit “1” because it is followed by a group of three digits “000”. It then matches the second digit “0” because it is followed by another group of three digits “000”. It continues to match every digit in the number string that is followed by a group of three digits. When it reaches the end of the string, it stops matching because the last digit “0” is not followed by a group of three digits.
Conclusion 1
We concluded that the regular expression (\d)(?=(?:\d{3})+(?!\d)) can be used to format a number string by adding commas every three digits. The regular expression matches every digit in the string except for the ones at the end that are not part of a group of three.
More Techniques
Negative Lookahead: This is similar to positive lookahead, but it matches if the pattern does NOT occur after the current position. It is denoted by (?!pattern).
Capturing Groups with Names: Instead of just using numbered capturing groups like $1, you can assign names to capturing groups for easier reference. For example, (?\d{4}) would match a four-digit number and capture it in the group named “year”.
Quantifiers: Quantifiers allow you to specify how many times a character or group should be matched. For example, a{3,5} would match “aaa”, “aaaa”, or “aaaaa”, but not “aa”.
Alternation: Alternation allows you to match one pattern OR another. It is denoted by the | symbol. For example, (cat|dog) would match “cat” or “dog”.
Character Classes: Character classes allow you to match a range of characters. For example, [a-z] would match any lowercase letter, and [0-9] would match any digit.
Non-Capturing Groups: Similar to capturing groups, non-capturing groups allow you to group parts of the pattern together without capturing the match. This can be useful for improving performance and simplifying the regular expression. Non-capturing groups are denoted by (?:pattern).
Anchors: Anchors allow you to match a pattern at a specific position in the string. The most common anchors are ^, which matches the start of a line, and $, which matches the end of a line.
Backreferences: Backreferences allow you to match a pattern that was previously matched by a capturing group. This can be useful for finding repeated patterns in a string. Backreferences are denoted by \number, where number is the number of the capturing group you want to reference.
Lookbehinds: Lookbehinds are similar to lookaheads, but they match a pattern that precedes the current position in the string. Lookbehinds are denoted by (?<=pattern) for a positive lookbehind and (?<!pattern) for a negative lookbehind.
Unicode Characters: Regular expressions can match any Unicode character by using escape sequences. For example, \p{L} matches any letter, and \p{N} matches any digit.
Character Classes: Character classes allow you to match a single character from a set of characters. They are denoted by square brackets, for example [aeiou] matches any vowel.
Alternation: Alternation allows you to match one of several alternatives. It is denoted by the pipe character |. For example, (cat|dog) matches either “cat” or “dog”.
Quantifiers: Quantifiers allow you to match a certain number of repetitions of a pattern. The most common quantifiers are * for zero or more, + for one or more, and ? for zero or one.
Dot: The dot . matches any character except a newline character.
Word Boundaries: Word boundaries allow you to match a pattern only when it appears at the beginning or end of a word. They are denoted by \b.
Greedy vs. Lazy Matching: By default, regular expressions are greedy and match as much as possible. You can use a ? after a quantifier to make it lazy and match as little as possible.
Character Escapes: Character escapes allow you to match characters that have special meaning in regular expressions. For example, to match a literal dot character, you need to escape it with a backslash: ..
Substitution: Regular expressions are often used to replace text. You can use the replace() method to substitute matches with other text.
Flags: Regular expressions can have flags that modify their behavior. The most common flags are i for case-insensitive matching and g for global matching.
Grouping: Grouping allows you to treat a pattern as a single unit and apply a quantifier to it. Groups are denoted by parentheses, for example a(bc)+ matches “abc”, “abcbc”, “abcbcbc”, and so on.
Conclution 2
얼마전 코딩테스트 보러갔다가 종이 시험을 보는것을 보고 1차로 놀랐다. 그리곤 문제를 보는데 1번부터 내 머리속에 아무것도 없는것을 느끼고 2차로 놀랐다. IDE의 힘을 빌려 AI HELPER의 빌려 살다보니 그렇다. 특히 정규식같은 부분은 한번 써놓고 오래 보질 않으니 다 휘발되고 머리에 남은게 없었다. 그렇게 계속 생각없이 살다가 Fiddler로 결제모듈을 뜯다보니 왠걸 갑자기 내가 정규식이 아닌 리스트로 풀었던 답이 딱 나오길래 글을 남긴다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
publicclassamount { publicstaticvoidmain(String[] args) { Stringstr="10000000"; StringformattedStr= str.replaceAll("(\\d)(?=(?:\\d{3})+(?!\\d))", "$1,"); // str의 한글자 한글자를 정규식을 대입해서 일치하면 캡처한 변수 $1로 바꾼다. // (\\d): 첫번째 차례인 문자가 숫자인경우, 값을 캡쳐한다. 일치하지않으면 다음 차례 문자를 검사한다. // (?=) : 전방탐색(positive lookahead)으로 str 끝까지 미리 일치하는지 검사 한다. // (?:) : 괄호안에 있어도 캡쳐하지 않는다는 뜻이다. // \\d{3}+ : 숫자 3개로 이루어진 세트가 여러개 있는것을 말한다. // (?!) : 이 다음이 아닌경우를 찾는다. // \\d : 숫자를 뜻한다. // 종합: 맨 처음부터 문자 한개를 잡고 그뒤로 숫자 3개가 붙어있는 세트가 쭉 이어지는지 검사한다. 딱 맞아 떨어지면 캡쳐해놓은 맨 처음 숫자에 ,를 더해서 교체한다. // 만약 딱 맞아 떨어지지 않으면 그냥 통과한다. System.out.println(formattedStr); } }
What is the difference between an enumeration and a structure in Swift?
In Swift, an enumeration (or “enum” for short) is a value type that represents a group of related values. Enums are defined using the enum keyword and can be used to define a set of possible values for a variable, for example, a list of error codes, or a list of days of the week. Enums can also have associated values, which can be used to store additional information for each case. A structure (or “struct” for short) is also a value type in Swift that represents a group of related values, but it can store multiple values of different types, similar to a class. Structures can have properties, methods, and initializers, and they can also conform to protocols.
요약: 열거형(“Enum”)은 관련 값 그룹 유형이며 값 집합을 정의하는데 사용한다. 구조체(“Struct”)는 관련 값 그룹 인것 맞지만 서로 다른 유형의 여러 값을 저장할 수 있으며, 속성, 메서드, 이니셜라이저를 갖고 프로토콜을 따를 수도 있다.
Can you give an example of using optional chaining in Swift?
Optional chaining in Swift is a way of calling properties, methods, or subscripts on an optional value, where the optional value may be nil. If the optional value is nil, the result of the optional chaining is nil, otherwise, it returns the result of the operation.
Here’s an example of using optional chaining in Swift:
classPerson { var name: String var address: Address? init(name: String) { self.name = name } } classAddress { var street: String var city: String init(street: String, city: String) { self.street = street self.city = city } } let john =Person(name: "John") iflet street = john.address?.street { print(street) } else { print("john does not have an address") } john.address =Address(street: "Jong-ro",city: "Seoul") iflet street = john.address?.street { print(street) } else { print("john does not have an address") }
Output:
1 2
john does not have an address Jong-ro
What is the purpose of the “defer” statement in Swift?
The “defer” statement in Swift is used to execute a block of code just before the current function returns. The code in the “defer” block is executed regardless of how the function returns, whether it returns normally, or due to an exception being thrown. The main use case for the “defer” statement is to perform cleanup tasks, such as freeing up resources, closing files, or releasing locks, even if the function exits early due to an error. This helps to simplify the code by centralizing the cleanup code in a single place and making it easier to understand the code flow.
요약: “defer”는 함수 반환 전 정상, 예외 여부와 관계없이 마지막에 실행되는것으로 오류로 인해 조기에 종료되더라도 파일닫기, 잠금해제같은 작업을 수행하기 위함이다.
Here’s an example of using the “defer” statement in Swift:
1 2 3 4 5 6 7 8
funcprocessFile(fileName: String) throws { let file =try openFile(fileName) defer { closeFile(file) } // perform some processing on the file try processContents(file) }
In this example, the openFile function is called to open a file, and the closeFile function is called in the defer block to close the file. The closeFile function is guaranteed to be called, even if the processContents function throws an error, because the defer block is executed just before the processFile function returns. This makes it easy to ensure that the file is always closed, even if an error occurs during the processing of the file.
What is the difference between a class and a structure in Swift?
In Swift, a class and a structure are both used to define custom data types, but they have some key differences. Classes are reference types, which means that when you assign a class instance to a variable, you are actually creating a reference to the instance, not a copy of it. This means that multiple variables can refer to the same instance, and changes to the instance made through one reference will be visible through all references. Structures, on the other hand, are value types, which means that when you assign a struct instance to a variable, you are creating a copy of the instance. This means that changes to the instance made through one reference will not affect other references to the same instance. Classes can also inherit from other classes and can be used to create objects, while structures cannot. Structures, however, are generally faster and more efficient than classes, especially when it comes to small instances, and they are also easier to manage and debug because they are self-contained.
요약: 구조체, 클래스 모두 데이터 유형 정의에 사용. 클래스를 인스턴스에 할당하면 복제가 아닌 참조를 만드는것이나, 구조는 복사본이 생성된다. 클래스는 다른 클래스에서 상속할 수 있고 객체를 만드는데 사용할 수 있지만 구조체는 그렇지 않다. 인스턴스가 작으면 구조체가 더 효율적일 수 있다.
Can you explain the difference between a class method and an instance method in Swift?
Class methods are called on the class itself, while instance methods are called on instances of the class. Class methods can be called without creating an instance of the class, while instance methods require an instance to be created. Class methods have the class keyword in their declaration, while instance methods do not.
클래스 메서드는 클래스 자체에서 인스턴스 할당 없이 호출되는 반면, 인스턴스 메서드는 클래스를 인스턴스에 할당 한 뒤에 그 인스턴스에서 호출된다.
1 2 3 4 5 6 7 8 9 10 11
classCar { staticfuncdisplayClassName() { print("Car") } funcdisplayInstanceName() { print("Instance of Car") } } Car.displayClassName() // Output: Car let myCar =Car() myCar.displayInstanceName() // Output: Instance of Car
Can you give an example of using generics in Swift?
// Example usage: let unsortedInts = [5, 3, 9, 2, 7] let sortedInts = sortArray(unsortedInts) print(sortedInts) // Output: [2, 3, 5, 7, 9]
let unsortedStrings = ["cat", "dog", "bird", "fish"] let sortedStrings = sortArray(unsortedStrings) print(sortedStrings) // Output: ["bird", "cat", "dog", "fish"]
함수 sortArray는 Comparable프로토콜을 따르는 제네릭 타입 T를 사용한다. Comparable을 따른다는것은 비교연산자를 사용할 수 있다는것이다.
What is the difference between optional binding and optional chaining in Swift?
Optional binding is used to safely unwrap an optional value and bind it to a new variable or constant, and it’s typically used when you need to perform operations on the unwrapped value. In this example, we use optional binding with if let to safely unwrap the optional value optionalName and bind it to the new constant name. If the optional value is nil, the code inside the else block will be executed. Here’s an example:
Optional chaining, on the other hand, is used to safely access a property or call a method on an optional value, without needing to first unwrap the value. In this example, we use optional chaining with the ? operator to safely access the street property of the person’s address property. If person.address is nil, the code inside the else block will be executed instead. Here’s an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
structPerson { var name: String var age: Int var address: Address? }
structAddress { var street: String var city: String var state: String }
let person =Person(name: "John", age: 30, address: Address(street: "123 Main St", city: "Anytown", state: "CA"))
iflet street = person.address?.street { // <== print("He lives on \(street).") } else { print("No address found.") }
반면 옵셔널체이닝은 안전하게 변수에 접근하기 위함이다.
Can you explain the difference between class inheritance and protocol-oriented programming in Swift?
Both class inheritance and protocol-oriented programming are ways to achieve code reuse and polymorphism in Swift, but they have different approaches and trade-offs.
Class inheritance is a mechanism where a subclass inherits properties and methods from a superclass. The subclass can also override and add its own properties and methods. Class inheritance is a form of vertical code reuse because it defines a hierarchy of classes with shared functionality.
=> 클래스는 일부를 공유하는 계층적 구조이므로 수직적인 상속 형태라고 볼 수 있다.
In this example, the Cat class inherits from the Animal class, which defines a name and sound property and a makeSound() method. The Cat class overrides the makeSound() method to provide its own implementation.
let cat =Cat(name: "Whiskers") cat.makeSound() // Output: "Whiskers purrs." (Example of overriden func)
Protocol-oriented programming, on the other hand, is a programming paradigm where functionality is defined in small, composable protocols that can be combined to create new types. This is a form of horizontal code reuse because it encourages the creation of many small protocols with specific functionality that can be combined and reused in different ways.
protocolMovable { var speed: Double { get } funcmove() }
classCar: Movable { var speed: Double init(speed: Double) { self.speed = speed } funcmove() { print("The car is moving at \(speed) mph.") } }
classPlane: Movable { var speed: Double init(speed: Double) { self.speed = speed } funcmove() { print("The plane is flying at \(speed) mph.") } }
let car =Car(speed: 60) let plane =Plane(speed: 500)
let vehicles: [Movable] = [car, plane]
for vehicle in vehicles { vehicle.move() }
While protocols and class inheritance can achieve similar results in some cases, they have different strengths and use cases.
One advantage of using protocols is that they allow for greater flexibility and modularity in your code. Protocols can be adopted by a wide range of types, including classes, structs, and enums. This means that you can create more specialized types that conform to a protocol, rather than having to subclass a more general type. This can help avoid issues with tight coupling and inheritance hierarchies that can arise with class inheritance.
Additionally, protocols can be used to achieve polymorphism across types that don’t share a common superclass. With protocols, you can define a common set of requirements that any conforming type must implement, allowing you to work with a wider range of types that share a particular behavior.
Another advantage of protocols is that they allow for composition over inheritance. With protocols, you can define small, modular interfaces that can be combined to create larger and more complex behaviors. This can make your code more flexible and easier to maintain, since you can reuse small pieces of functionality in different contexts.
In summary, while protocols and class inheritance can both achieve code reuse and polymorphism, protocols have different strengths and use cases, including greater flexibility and modularity, better support for polymorphism across different types, and the ability to compose smaller pieces of functionality into larger behaviors.
=> 프로토콜은 더 큰 유연성과 모듈성을 허용하며, 클래스 뿐만 아니라 구조체, 열거형 등에서도 사용하며 하위 클래스로 분류하는게 아닌 프로토콜을 준수하는 유형으로 취급하여 클래스 상속에서 발생할 수 있는 강결합 및 상속 계층 문제를 방지한다. => 클래스 상속과 프로토콜을 동시에 사용이 가능하다.
How does Swift handle type casting?
In Swift, type casting is the process of checking the type of an instance at runtime and converting it to another type if possible. There are two types of type casting in Swift: upcasting and downcasting.
Upcasting is the process of converting an instance of a subclass to an instance of its superclass. Since a subclass is guaranteed to have all the properties and methods of its superclass, upcasting is always safe in Swift.
Here’s an example of upcasting and downcasting in Swift:
classHuman { let name: String init(name: String) { self.name = name } }
classTeacher: Human { let school: String init(name: String, school: String) { self.school = school super.init(name: name) } }
classStudent: Human { let school: String init(name: String, school: String) { self.school = school super.init(name: name) } } // Upcasting to the Human superclass let human: Human=Teacher(name: "John Doe", school: "Acme High School") let human2: Human=Student(name: "Jimmy Yo", school: "Batman School")
var humanList = [Human]()
humanList.append(human) humanList.append(human2)
// Downcasting to the Teacher subclass for human in humanList { iflet teacher = human as?Teacher { print("\(teacher.name) is teacher") } elseiflet student = human as?Student { print("\(student.name) is student") } else { print("\(human.name) is something else") } }
Downcasting is the process of converting an instance of a superclass to an instance of a subclass. Since a superclass does not necessarily have all the properties and methods of its subclasses, downcasting is not always safe in Swift. To perform a downcast safely, you need to use the optional type-casting operator as? or the forced type-casting operator as!.
=> type을 쓰면 어떤 클래스인지 정확하게 알 수 있다. => 업캐스팅은 서브클래스를 어퍼클래스로 취급하고싶을때 사용 (포장) => 다운캐스팅은 업캐스팅된 서브클래스를 다시 서브클래스로 취급하고싶을때 사용 (포장 제거)
Can you give an example of using the guard statement in Swift to handle optional values?
guard. 진짜면 나간다.
1 2 3 4 5 6 7 8 9 10
funcgreet(name: String?) { guardlet name = name else { print("No name provided") return } print("Hello, \(name)!") }
greet(name: "John") // prints "Hello, John!" greet(name: nil) // prints "No name provided"
greet 함수는 옵셔널 String을 파라메터로 받는다. 이 함수에서 guard는 name 파라메터가 nil인지 검사하고 조건이 true 이면 gurad를 나가고, false 이면(nil이면) guard 안을 돈다.
Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.