개인적으로, 어떤 알고리즘을 자연어로 표현하는 것 보다, 그냥 코딩하는 게 더 쉽다.
이 문서에서 '알고리즘'은, '구현 방식 기획'과 의미가 비슷하다.
public static string ChangeExtension(string originalPath, string? extension)
{
int lastDotIndex = originalPath.LastIndexOf('.');
if (lastDotIndex == -1)
{
return string.Concat(originalPath, extension);
}
else
{
return string.Concat(originalPath.AsSpan(0, lastDotIndex), extension);
}
}
주어진 정보:
실행 과정:
1.의 결과
가 -1 과 같은지 확인한다.
1.의 결과
- 1)번째까지 잘라낸다.2.ii.a.의 결과
와 '새 확장자'를 연결하고, 그 결과를 반환한다.사실 저 자연어 알고리즘도 썩 마음에 드는 건 아닌데...일단 저 정도로 할 수 있는 걸 목표로 하자.
/// <param name="originalPath">원본 파일 경로</param>
/// <param name="extension">교체할 새 확장자</param>
public static string ChangeExtension(string originalPath, string? extension)
{
// 1. '원본 파일 경로'에서 '.'의 마지막 위치를 얻는다.
int lastDotIndex = originalPath.LastIndexOf('.');
// 2. `1.의 결과`가 -1 과 같은지 확인한다.
if (lastDotIndex == -1)
{
// 1. 맞다면, '원본 파일 경로'와 '새 확장자'를 연결하고, 그 결과를 반환한다.
return string.Concat(originalPath, extension);
}
else
{
// 2. 아니라면,
// 1. '원본 파일 경로'를 0번째부터 (`1.의 결과` - 1)번째까지 잘라낸다.
var v1 = originalPath.AsSpan(0, lastDotIndex);
// 2. `2.ii.a.의 결과`와 '새 확장자'를 연결하고, 그 결과를 반환한다.
return string.Concat(v1, extension);
}
}
저 '분리' 부터 구현해야 하려나.
return string.Concat(originalPath.AsSpan(0, lastDotIndex), extension);
↓
var v1 = originalPath.AsSpan(0, lastDotIndex);
return string.Concat(v1, extension);
이것 말고도, 분리해야 할 게 많겠지..
public static string ChangeExtension(string originalPath, string? extension)
{
int lastDotIndex = originalPath.LastIndexOf('.');
if (lastDotIndex == -1)
{
return string.Concat(originalPath, extension);
}
else
{
return string.Concat(originalPath.AsSpan(0, lastDotIndex), extension);
}
}
public static string ChangeExtension(string originalPath, string? extension)
{
//
int lastDotIndex = originalPath.LastIndexOf('.');
//
bool b1_1 = lastDotIndex == -1;
if (b1_1)
{
//
return string.Concat(originalPath, extension);
}
else
{
//
var v2_1 = originalPath.AsSpan(0, lastDotIndex);
//
return string.Concat(v2_1, extension);
}
}
using RoslynQuoter;
namespace Nemonuri.RoslynQuoters;
/// <summary>
/// 프로그램
/// </summary>
public static class Program
{
/// <summary>
/// 이 프로그램의 진입점입니다.
/// </summary>
/// <param name="args">명령어</param>
public static void Main(string[] args)
{
if (CommandParsingTheory.Parse(args) is not { } parseResult) { return; }
string csharpText = File.ReadAllText(parseResult.TargetFile.FullName);
Quoter quoter = new Quoter();
Quoter.ApiCall apiCall = quoter.Quote(csharpText);
Console.WriteLine(apiCall.ToString());
}
}
using RoslynQuoter;
namespace Nemonuri.RoslynQuoters;
/// <summary>
/// 프로그램
/// </summary>
public static class Program
{
/// <summary>
/// 이 프로그램의 진입점입니다.
/// </summary>
/// <param name = "args">명령어</param>
public static void Main(string[] args)
{
//
bool b1_1 = CommandParsingTheory.Parse(args)is not { } parseResult;
if (b1_1)
{
//
return;
}
//
var v2_1 = parseResult.TargetFile.FullName;
//
string csharpText = File.ReadAllText(v2_1);
//
Quoter quoter = new Quoter();
//
Quoter.ApiCall apiCall = quoter.Quote(csharpText);
//
var v2_2 = apiCall.ToString();
//
Console.WriteLine(v2_2);
}
}
<|im_start|>system
You are a professional senior C# programmer.
<RULE>
1. Add a comment as sentence in Korean for each code line of the given method name.
2. User will input code in <CODE/> block, and method name in <METHOD_NAME/> block.
</RULE>
<|im_end|>
<|im_start|>user
<CODE>
/// <summary>
/// 원본 파일 경로의 확장자를 새 것으로 교체합니다.
/// </summary>
/// <param name = "originalPath">원본 파일 경로</param>
/// <param name = "extension">교체할 새 확장자</param>
/// <returns>확장자가 교체된 파일 경로</returns>
public static string ChangeExtension(string originalPath, string? extension)
{
//
int lastDotIndex = originalPath.LastIndexOf('.');
//
bool b1_1 = lastDotIndex == -1;
if (b1_1)
{
//
return string.Concat(originalPath, extension);
}
else
{
//
var v2_1 = originalPath.AsSpan(0, lastDotIndex);
//
return string.Concat(v2_1, extension);
}
}
</CODE>
<METHOD_NAME>
ChangeExtension
</METHOD_NAME>
<|im_end|>
<|im_start|>assistant
/// <summary>
/// 원본 파일 경로의 확장자를 새 것으로 교체합니다.
/// </summary>
/// <param name="originalPath">원본 파일 경로</param>
/// <param name="extension">교체할 새 확장자</param>
/// <returns>확장자가 교체된 파일 경로</returns>
public static string ChangeExtension(string originalPath, string? extension)
{
// 원본 파일 경로에서 마지막 '.' 인덱스를 찾습니다.
int lastDotIndex = originalPath.LastIndexOf('.');
// 만약 '.'이 없다면 확장자를 추가합니다.
bool b1_1 = lastDotIndex == -1;
if (b1_1)
{
// 원본 파일 경로에 새 확장자를 연결하여 반환합니다.
return string.Concat(originalPath, extension);
}
else
{
// '.'이 있다면 '.' 전까지의 부분을 가져옵니다.
var v2_1 = originalPath.AsSpan(0, lastDotIndex);
// 원본 파일 경로에서 확장자를 제거한 후 새 확장자를 추가하여 반환합니다.
return string.Concat(v2_1, extension);
}
}
<|im_start|>system
You are a professional senior C# programmer.
<RULE>
1. Add a comment as sentence in Korean for each code line of the given method name.
2. User will input code in <CODE/> block, and method name in <METHOD_NAME/> block.
</RULE>
<|im_end|>
<|im_start|>user
<CODE>
using RoslynQuoter;
/// <summary>
/// 프로그램
/// </summary>
public static class Program
{
/// <summary>
/// 이 프로그램의 진입점입니다.
/// </summary>
/// <param name = "args">명령어</param>
public static void Main(string[] args)
{
//
bool b1_1 = CommandParsingTheory.Parse(args)is not { } parseResult;
if (b1_1)
{
//
return;
}
//
var v2_1 = parseResult.TargetFile.FullName;
//
string csharpText = File.ReadAllText(v2_1);
//
Quoter quoter = new Quoter();
//
Quoter.ApiCall apiCall = quoter.Quote(csharpText);
//
var v2_2 = apiCall.ToString();
//
Console.WriteLine(v2_2);
}
}
</CODE>
<METHOD_NAME>
Main
</METHOD_NAME>
<|im_end|>
<|im_start|>assistant
using RoslynQuoter;
/// <summary>
/// 프로그램
/// </summary>
public static class Program
{
/// <summary>
/// 이 프로그램의 진입점입니다.
/// </summary>
/// <param name = "args">명령어</param>
public static void Main(string[] args)
{
// 명령어를 파싱하여 결과가 없는지 확인합니다. 결과가 있으면 true, 없으면 false
bool b1_1 = CommandParsingTheory.Parse(args)is not { } parseResult;
if (b1_1)
{
// 파싱된 결과가 없는 경우 메서드를 종료합니다.
return;
}
// 파싱된 결과에서 대상 파일의 전체 경로를 가져옵니다.
var v2_1 = parseResult.TargetFile.FullName;
// 해당 경로에 있는 C# 텍스트를 읽어옵니다.
string csharpText = File.ReadAllText(v2_1);
// RoslynQuoter를 사용하여 C# 코드를 변환합니다.
Quoter quoter = new Quoter();
Quoter.ApiCall apiCall = quoter.Quote(csharpText);
// 변환된 결과를 문자열로 변환합니다.
var v2_2 = apiCall.ToString();
// 변환된 결과를 콘솔에 출력합니다.
Console.WriteLine(v2_2);
}
}
일단, 예시 1만 해보자. 수작업으로.
/// <summary>
/// 원본 파일 경로의 확장자를 새 것으로 교체합니다.
/// </summary>
/// <param name="originalPath">원본 파일 경로</param>
/// <param name="extension">교체할 새 확장자</param>
/// <returns>확장자가 교체된 파일 경로</returns>
public static string ChangeExtension(string originalPath, string? extension)
{
// 원본 파일 경로에서 마지막 '.' 인덱스를 찾습니다.
int lastDotIndex = originalPath.LastIndexOf('.');
// 만약 '.'이 없다면 확장자를 추가합니다.
bool b1_1 = lastDotIndex == -1;
if (b1_1)
{
// 원본 파일 경로에 새 확장자를 연결하여 반환합니다.
return string.Concat(originalPath, extension);
}
else
{
// '.'이 있다면 '.' 전까지의 부분을 가져옵니다.
var v2_1 = originalPath.AsSpan(0, lastDotIndex);
// 원본 파일 경로에서 확장자를 제거한 후 새 확장자를 추가하여 반환합니다.
return string.Concat(v2_1, extension);
}
}
질문1: 결과가 마음에 드는가? 아니라면, 그 이유는 무엇인가?
답변1
질문2
답변2: 있다. '메서드'다! 각 줄의 Expression 을, 메서드로 감싸고, 각 메서드에 엄격한 형식을 가진 주석(<summary>
)을 LLM에게 달게 하자!
수작업으로 진행했다.
public static string ChangeExtension(string originalPath, string? extension)
{
int lastDotIndex = originalPath.LastIndexOf('.');
if (lastDotIndex == -1)
{
return string.Concat(originalPath, extension);
}
else
{
return string.Concat(originalPath.AsSpan(0, lastDotIndex), extension);
}
}
/// <summary>
/// 원본 파일 경로의 확장자를 새 것으로 교체합니다.
/// </summary>
/// <param name = "originalPath">원본 파일 경로</param>
/// <param name = "extension">교체할 새 확장자</param>
/// <returns>확장자가 교체된 파일 경로</returns>
public static string ChangeExtension(string originalPath, string? extension)
{
int lastDotIndex = M1(originalPath);
if (M2(lastDotIndex))
{
return M3(originalPath, extension);
}
else
{
Span<char> v2_1 = M4(originalPath, lastDotIndex);
return M5(v2_1, extension);
}
}
public static int M1(string originalPath)
{
return originalPath.LastIndexOf('.');
}
public static bool M2(int lastDotIndex)
{
return lastDotIndex == -1;
}
public static string M3(string originalPath, string extension)
{
return string.Concat(originalPath, extension);
}
public static Span<char> M4(string originalPath, int lastDotIndex)
{
return originalPath.AsSpan(0, lastDotIndex);
}
public static string M5(Span<char> v2_1, string extension)
{
return string.Concat(v2_1, extension);
}
LLM을 이용해 주석을 달아보았다.
입력 프롬프트
<|im_start|>system
You are a professional C# programmer.
<RULE>
1. Write `<summary>` style comment to each method, in Korean.
</RULE>
<|im_end|>
<|im_start|>user
```csharp
/// <summary>
/// 원본 파일 경로의 확장자를 새 것으로 교체합니다.
/// </summary>
/// <param name = "originalPath">원본 파일 경로</param>
/// <param name = "extension">교체할 새 확장자</param>
/// <returns>확장자가 교체된 파일 경로</returns>
public static string ChangeExtension(string originalPath, string? extension)
{
int lastDotIndex = M1(originalPath);
if (M2(lastDotIndex))
{
return M3(originalPath, extension);
}
else
{
Span<char> v2_1 = M4(originalPath, lastDotIndex);
return M5(v2_1, extension);
}
}
public static int M1(string originalPath)
{
return originalPath.LastIndexOf('.');
}
public static bool M2(int lastDotIndex)
{
return lastDotIndex == -1;
}
public static string M3(string originalPath, string extension)
{
return string.Concat(originalPath, extension);
}
public static Span<char> M4(string originalPath, int lastDotIndex)
{
return originalPath.AsSpan(0, lastDotIndex);
}
public static string M5(Span<char> v2_1, string extension)
{
return string.Concat(v2_1, extension);
}
```
<|im_end|>
<|im_start|>assistant
/// <summary>
/// 원본 파일 경로의 확장자를 새 것으로 교체합니다.
/// </summary>
/// <param name="originalPath">원본 파일 경로</param>
/// <param name="extension">교체할 새 확장자</param>
/// <returns>확장자가 교체된 파일 경로</returns>
public static string ChangeExtension(string originalPath, string? extension)
{
int lastDotIndex = M1(originalPath);
if (M2(lastDotIndex))
{
return M3(originalPath, extension);
}
else
{
Span<char> v2_1 = M4(originalPath, lastDotIndex);
return M5(v2_1, extension);
}
}
/// <summary>
/// 원본 파일 경로에서 마지막에 있는 '.'의 인덱스를 반환합니다.
/// </summary>
/// <param name="originalPath">원본 파일 경로</param>
/// <returns>'.'의 인덱스 또는 -1(확장자가 없는 경우)</returns>
public static int M1(string originalPath)
{
return originalPath.LastIndexOf('.');
}
/// <summary>
/// '.'이 원본 파일 경로에 있는지 확인합니다.
/// </summary>
/// <param name="lastDotIndex">'.'의 인덱스</param>
/// <returns>확장자가 없는 경우 true, 그렇지 않으면 false</returns>
public static bool M2(int lastDotIndex)
{
return lastDotIndex == -1;
}
/// <summary>
/// 원본 파일 경로에서 확장자를 제외한 부분과 새 확장자를 결합하여 새로운 파일 경로를 생성합니다.
/// </summary>
/// <param name="originalPath">원본 파일 경로</param>
/// <param name="extension">교체할 새 확장자</param>
/// <returns>확장자가 교체된 파일 경로</returns>
public static string M3(string originalPath, string extension)
{
return string.Concat(originalPath, extension);
}
/// <summary>
/// 원본 파일 경로에서 확장자를 제외한 부분을 가져옵니다.
/// </summary>
/// <param name="originalPath">원본 파일 경로</param>
/// <param name="lastDotIndex">'.'의 인덱스</param>
/// <returns>확장자를 제외한 부분의 Span<char></returns>
public static Span<char> M4(string originalPath, int lastDotIndex)
{
return originalPath.AsSpan(0, lastDotIndex);
}
/// <summary>
/// 확장자를 제외한 부분과 새 확장자를 결합하여 새로운 파일 경로를 생성합니다.
/// </summary>
/// <param name="v2_1">확장자를 제외한 부분의 Span<char></param>
/// <param name="extension">교체할 새 확장자</param>
/// <returns>확장자가 교체된 파일 경로</returns>
public static string M5(Span<char> v2_1, string extension)
{
return string.Concat(v2_1, extension);
}
앞의 것도 나쁘진 않은데,
'단계'를 약간 수정하면 좋을 것 같아.
그런데, Step2.1.의 설명이 뭔가 이상하다?
Step2. 의 맞다·아니다 도 완전 반대야.
LLM을 한번 더 실행해보자. 동일한 입력 프롬프트를 사용해서.
/// <summary>
/// 원본 파일 경로의 확장자를 새 것으로 교체합니다.
/// </summary>
/// <param name="originalPath">원본 파일 경로</param>
/// <param name="extension">교체할 새 확장자</param>
/// <returns>확장자가 교체된 파일 경로</returns>
public static string ChangeExtension(string originalPath, string? extension)
{
int lastDotIndex = M1(originalPath);
if (M2(lastDotIndex))
{
return M3(originalPath, extension);
}
else
{
Span<char> v2_1 = M4(originalPath, lastDotIndex);
return M5(v2_1, extension);
}
}
/// <summary>
/// 원본 파일 경로에서 마지막 점의 인덱스를 반환합니다.
/// </summary>
/// <param name="originalPath">원본 파일 경로</param>
/// <returns>마지막 점의 인덱스</returns>
public static int M1(string originalPath)
{
return originalPath.LastIndexOf('.');
}
/// <summary>
/// 주어진 인덱스가 -1인지 확인합니다.
/// </summary>
/// <param name="lastDotIndex">점의 인덱스</param>
/// <returns>점이 없으면 true, 있으면 false</returns>
public static bool M2(int lastDotIndex)
{
return lastDotIndex == -1;
}
/// <summary>
/// 원본 파일 경로와 새 확장자를 결합하여 새로운 파일 경로를 반환합니다.
/// </summary>
/// <param name="originalPath">원본 파일 경로</param>
/// <param name="extension">새 확장자</param>
/// <returns>확장자가 교체된 파일 경로</returns>
public static string M3(string originalPath, string extension)
{
return string.Concat(originalPath, extension);
}
/// <summary>
/// 원본 파일 경로에서 마지막 점까지의 부분을 Span<char>로 반환합니다.
/// </summary>
/// <param name="originalPath">원본 파일 경로</param>
/// <param name="lastDotIndex">점의 인덱스</param>
/// <returns>Span<char> 형태의 파일 경로 부분</returns>
public static Span<char> M4(string originalPath, int lastDotIndex)
{
return originalPath.AsSpan(0, lastDotIndex);
}
/// <summary>
/// Span<char>와 새 확장자를 결합하여 새로운 파일 경로를 반환합니다.
/// </summary>
/// <param name="v2_1">Span<char> 형태의 파일 경로 부분</param>
/// <param name="extension">새 확장자</param>
/// <returns>확장자가 교체된 파일 경로</returns>
public static string M5(Span<char> v2_1, string extension)
{
return string.Concat(v2_1, extension);
}